it-swarm.com.ru

Как инициализировать контроллеры JavaFX с тем же объектом модели?

Сценарий

Я создаю графический интерфейс, где несколько видов ссылаются на один и тот же объект модели. 

К чему я привык 

В Swing, если я хочу, чтобы все представления ссылались на одну и ту же модель, я бы передал модель в конструктор. 

Чем я сейчас занимаюсь

В JavaFX я передаю модель, используя метод установки в представлениях/контроллерах (меню, разделенные панели, вкладки, ...), после загрузки каждого представления/контроллера. Я нахожу это очень липким и громоздким. Кроме того, я считаю, что это не будет работать, потому что в определенных ситуациях мне нужно, чтобы модель уже существовала в контроллере до инициализации некоторых виджетов контроллера.

Lackluster Alternatives

(Примечание: я ссылаюсь на следующие вопросы: 

  • Javafx 2.0 How-to Application.getParameters () в файле Controller.Java
  • Передача параметров JavaFX FXML
  • Несколько FXML с контроллерами, общий объект
  • Передача параметров в контроллер при загрузке FXML )

  • Внедрение зависимости

    • Я посмотрел на этом сайте, http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/ , и я немного заглянул в Google Guice , но я не вижу способа упрощенно дать каждому представлению/контроллеру JavaFX один и тот же объект модели. Казалось, что инъекция будет вводить разные модели для каждого вида/контроллера.
  • Сохранение объекта модели в виде публичной статической переменной

    • Это вариант, но на данный момент мне не нравится идея иметь открытую и доступную публичную статическую модель. Очевидно, я могу сделать это частной статической переменной и иметь геттеры и сеттеры, но эта идея мне тоже не очень нравится. 
  • Передача параметров от вызывающего к контроллеру

    • Я хочу, чтобы каждый контроллер загружал себя в своем конструкторе, и я хочу, чтобы каждый пользовательский контроллер автоматически вставлялся в его родительский контроллер. Например, вкладка обзора карты загружается так:

      public CardOverviewTab() {
          FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("card_overview_tab.fxml"));
          fxmlLoader.setRoot(content);
          fxmlLoader.setController(this);
      
          try {
              fxmlLoader.load();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
    • А в контроллере SingleGameSetup вкладка обзора карты автоматически вводится в переменную:

      public class SingleGameSetupController extends AnchorPane {
      
          @FXML private CardOverviewTab cardOverviewTab;
      
          // Rest of the class
      }
      
    • И часть fxml, содержащая вкладку обзора карты, выглядит следующим образом:

      <CardOverviewTab fx:id="cardOverviewTab" />
      
    • Таким образом, мне не нужно беспокоиться о ручной загрузке контроллера, но у меня все еще есть проблема настройки модели.

  • Настройка контроллера на FXMLLoader

    • Эта опция похожа на ту, к которой я привык, передавая модель в качестве параметра в конструктор, но у нее все еще есть проблема загрузки контроллеров вручную с помощью FXMLLoader. 
  • Event Bus

    • Я не слишком много читал об этом, но из того, что я прочитал, шина событий кажется неактивной и устаревшей.
  • Одиночка

    • Это похоже на общедоступную статическую ссылку на объект модели, которую контроллеры могут получить, но опять же я ищу лучшее решение. Плюс, я не хочу модель синглтона.

Что я ищу

Есть ли способ передать объект модели менее громоздким способом? Я ищу способ, который так же прост, как передача модели в конструктор, но я не хочу иметь дело с ручной загрузкой контроллеров через FXMLLoader или настройкой модели после загрузки контроллеров. Возможно, иметь класс для извлечения модели - это лучший вариант, но я спрашиваю на всякий случай, если есть лучший способ.

23
j will

Обновление

В дополнение к afterburner.fx, также оформить заказ Gluon Ignite :

Gluon Ignite позволяет разработчикам использовать популярные структуры внедрения зависимостей в своих приложениях JavaFX, в том числе внутри своих контроллеров FXML. Gluon Ignite создает общую абстракцию над несколькими популярными инфраструктурами внедрения зависимостей (в настоящее время Guice, Spring и Dagger, но мы планируем добавить больше, когда потребность станет очевидной). Благодаря полной поддержке JSR-330 Gluon Ignite делает внедрение зависимостей в приложениях JavaFX тривиальным.

Инжекция объектов модели в контроллеры также осуществляется через @Inject, аналогично afterburner.fx.

Предлагаемый подход

Поскольку вы, похоже, ищете среду внедрения зависимостей, я думаю, что ваш лучший вариант - использовать afterburner.fx framework .

afterburner.fx предоставляет способ внедрения объектов модели в ваши контроллеры JavaFX, используя стандартную аннотацию Java @Inject

Альтернативные системы впрыска зависимостей

Spring большой и сложный, и, если вам не нужно много других его функций для вашего приложения, его не следует рассматривать из-за его сложности.

Guice намного проще, чем Spring, и является разумным выбором, если вам нужна инфраструктура внедрения зависимостей с многочисленными функциями, такими как классы провайдеров. Но, судя по всему, вам не нужны все функции, которые предоставляет Guice, поскольку вы просто хотите обойти одноэлементные экземпляры объектов в вашем приложении без явного их поиска.

Итак, попробуйте afterburner.fx и посмотрите, подходит ли он вам.

afterburner.fx Пример кода

Вот пример внедрения экземпляра модели (NotesStore) в контроллер с использованием afterburner.fx. Образец напрямую скопирован из afterburner.fx документации .

import com.airhacks.afterburner.views.FXMLView;

public class NoteListView extends FXMLView {
    //usually nothing to do, FXML and CSS are automatically
    //loaded and instantiated
}

public class AirpadPresenter implements Initializable {    
    @Inject // injected by afterburner, zero configuration required
    NotesStore store;

    @FXML // injected by FXML
    AnchorPane noteList;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        //view constructed from FXML
        NoteListView noteListView = new NoteListView();

        //fetching and integrating the view from FXML
        Parent view = noteListView.getView();
        this.noteList.getChildren().add(view);
    }
}

followme.fx - это базовый пример приложения, демонстрирующий, как использовать afterburner.fx. У меня было несколько проблем с запуском followme.fx из коробки из-за несовместимости с зависимостями Maven, поэтому я раздвоил его код и исправил некоторые проблемы, которые мешали мне использовать его из коробки.

Ответы на дополнительные вопросы из комментариев

Итак, из примера NoteStore вы говорите, что все, что мне нужно сделать, это добавить зависимость фреймворка Afterburner и поместить @Inject в мою переменную модели?

Нет, вам также необходимо создать связанный класс, расширяющий FXMLView, и создать его с помощью нового вызова (аналогично тому, как NotesListView создается в приведенном выше примере кода). Если вы заинтересованы в продолжении исследования инфраструктуры afterburner.fx, используйте проект followme.fx в качестве основы, поскольку он предоставляет полный исходный код для очень простого исполняемого примера, использующего эту среду. 

Я попробовал Google Guice и получил его на работу. , , в конструкторе вы увидите, что объект настроек игры вводится вручную.

Я не думаю, что вы должны использовать инжектор Guice таким образом. Я думаю, что вы можете установить фабрику контроллера на экземпляре FXMLLoader, чтобы инициировать инъекцию. Вот как это делает FXMLView в afterburner.fx. Точная деталь механизма впрыска, используемого в Guice, будет отличаться от механизма afterburner.fx, но я думаю, что общая концепция установки фабрики контроллера остается схожей.

В ответ на: Связывание FXML и Контроллера в конфигурации модуля Guice есть демонстрация фабрики контроллеров набора с использованием FXML и Guice.

Обидно, что нет более простого способа сделать это, который не вызывает у вас столько трудностей.

Как несущественное личное замечание, я в некоторой степени амбивалентен в отношении структуры внедрения зависимостей в целом. Конечно, они могут помочь, но много раз для простых вещей я часто в порядке с одноэлементным методом getInstance, а не с более сложной структурой. Тем не менее, я вижу, как в больших проектах они могут быть полезны, и, безусловно, они очень популярны в определенных средах Java.

15
jewelsea

Я исследовал эту проблему и обнаружил, что внедрение зависимостей вместе с синглтоном (для модели) - мой оптимальный дизайн. Я узнал, что наличие метода установки для модели в каждом из моих контроллеров является формой внедрения зависимостей, как и передача модели через конструктор. Spring и Guice являются каркасом, чтобы помочь этому. 

Я попытался использовать Spring, как указано здесь: http://www.zenjava.com/2011/10/23/better-controller-injection/ , но я столкнулся с проблемой, когда класс конфигурации попытался загрузить контроллер, не удалось загрузить элементы-контроллеры. Возможно, это не было проблемой Spring, но я подумал, что мне было все равно, чтобы тратить время на то, чтобы все заработало. Кроме того, мне пришлось бы просмотреть все файлы контроллера и отредактировать способ их создания.

Обыскав и много прочитав, я обнаружил, что эта статья лучше всего отражает то, что я хотел бы сделать: https://forums.Oracle.com/thread/2301217?tstart=0 . Поскольку статья ссылается на предлагаемые улучшения, эти улучшения еще не существуют в JavaFX. Но когда они это сделают, я буду их реализовывать. Просто для примера, возможность внедрить модель через fxml:

<usevar name="model" type="com.mycom.myapp.ModelObject"/>
0
j will

В DataFX Framework появился новый API под названием DataFX-Flow. С этим API вы можете просто внедрить объекты в контроллер представления. Кроме того, здесь поддерживаются области применения Afterburner.fx. Вы можете найти пример здесь: http://www.guigarage.com/2013/12/datafx-controller-framework-preview/

0
Hendrik Ebbers