Этот вопрос, вероятно, слишком основан на мнениях для этого форума, но я думаю, что мнения, которые я здесь выражаю, достаточно широко распространены, чтобы это было приемлемым.
Почему статические поля плохая идея
Предположим, вы сделали то, что вы пытаетесь сделать здесь, и это было разрешено работать. Представьте, что ваш босс приходит в ваш офис через 6 месяцев и говорит что-то вроде «Клиенты любят приложение, но они хотят иметь возможность открывать несколько редакторов узлов одновременно. Можете ли вы сделать приложение, чтобы они могли открывать новые окна, в каждом из которых есть редактор узлов?
Это происходит в реальной жизни все время.
Так что в теории это должно быть легко. У вас есть FXML, который определяет макет, и контроллер, чем определяет связанную с ним логику. Итак, все, что вам нужно сделать, это иметь обработчик события (например, в меню), который создает новый Stage
и Scene
, снова загружает FXML и устанавливает корень сцены в корень FXML.
За исключением того, что он не работает. Проблема в том, что вы делаете корневую панель static
. Поэтому, когда вы снова загрузили FMXL, он заменил текущее значение canvas
новым с недавно загруженного FXML. Если вы сейчас попытаетесь добавить новые элементы в верхний слой , вы не сможете.
Итак, теперь вы должны реорганизовать код, так что canvas
является переменной экземпляра. Что еще хуже, вы публично выставили его с помощью метода static getCanvas()
, который можно было бы назвать где угодно в вашем приложении. Поэтому вы должны отслеживать все эти призывы. И вы попадаете в 3 часа ночи, пытаясь сгладить всю вашу кодовую базу, чтобы выяснить, что вам нужно изменить.
Проблема (одна проблема) с созданием статических вещей заключается в том, что вы обязуетесь иметь только одну копию этой переменной и, по сути, вы обязуетесь на это на все время (или обязуетесь переписывать большую часть приложения, если вы меняете свой разум). Это решение протекает вверх: только когда у вас есть один холст, у вас может быть только один контроллер, поэтому вы можете иметь только один редактор узлов. Вы по существу не допустили, чтобы код, который вы написали, был повторно использован.
Ваше основное предположение здесь неверно. Ключевое слово static
действительно не имеет никакого отношения к Доступность: это около scope. Поле static
относится к классу: нестатическое поле относится к каждому экземпляру этого класса. Вы сделали доступным поле, указав общедоступный метод getCanvas()
и сделав область действия этого метода - доступным классом (сделав класс общедоступным). Если canvas
- это переменная экземпляра, вы можете сделать ее доступной, аналогично определяя общедоступный метод getCanvas()
и делая область действия этого метода - теперь доступен CanvasController
экземпляр. Таким образом, вы загружаете FXML, извлекаете контроллер, вызывая getController()
на FXMLLoader
и передавая экземпляр контроллера тому, кому нужен доступ к нему. Обратите внимание, что теперь у вас есть контроль над кодом, кто получил ссылку на контроллер, и если вам нужен какой-либо рефакторинг, у вас есть хотя бы путь для поиска, где используется контроллер.
Вы упоминаете «JavaFX не имеет NodeProperty
или AnchorPaneProperty
:. Он имеет ObjectProperty
, так что вы можете использовать ObjectProperty<Node>
или ObjectProperty<AnchorPane>
, если вы хотите/потребность
В целом, однако, Я думаю, что неплохо также выставлять элементы представления (например, ваш canvas
) вне контроллера и даже делиться контроллерами с другими частями приложения. Вместо этого вы должны использовать подход типа MVC. Что отсутствует в вашем вопросе это «M»: модель . Таким образом, ваша модель будет использовать объектную модель wit h классы, такие как Image
, Scale
, Composite
и т. д. Классы должны использовать JavaFX properties, чтобы сделать их наблюдаемыми и использовать ObservableList
(и аналогичные) для хранения коллекций этих объектов и отношений между ними. Создайте экземпляр одной модели и передайте ее контроллеру. Контроллер может наблюдать данные в модели и обновлять представление, если он изменяется, и обновлять модель в соответствии с пользовательским вводом. Затем передайте экземпляр модели с другими частями приложения, которым необходим доступ к нему. Это упрощает обслуживание: если вы решите, например, canvas
должен быть Pane
вместо AnchorPane
, это изменение теперь ограничивается вашим FXML и контроллером и не просачивается на остальную часть вашего приложения. Пример (намного проще) MVC (действительно больше MVP) в JavaFX находится в this question.
Вы действительно объяснили это очень хорошо, спасибо. Этот пример будущего сценария, который вы использовали, действительно помог поставить проблему с статическими полями, однако это единственное, что меня путает с шаблоном MVC. Используя JavaFx, у меня будет «NodeController» и «NodeModel» - что бы я создал? Есть ли у меня еще один «соединитель» класса «Узел», который объединяет эти два вместе? – user3668541
Если вы используете FXML, существует несколько разных способов: либо создайте модель, создайте контроллер ('NodeController nc = new NodeController (nodeModel)'), создайте «FXMLLoader» и установите контроллер «вручную» ('loader.setController (НЗ);'). Затем получите представление из «FXMLLoader», вызвав 'load()'. В качестве альтернативы, установите фабрику контроллера на «FXMLLoader», и пусть «FXMLLoader» создаст для вас контроллер. –
Обычно я не создавал класс, который инкапсулирует эту проводку, потому что почти все это обрабатывается FXMLLoader, но вам нечего остановить. Не уверен, что я знаю эту правильную сложность примера для вас: возможно, https://github.com/james-d/TicTacToe. Здесь «Игра» действует как общая модель, а различные компоненты внутри нее имеют свою собственную структуру MVC. (Я написал это некоторое время назад, так что это может быть не идеально;).) –