Подтвердить что ты не робот

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

Сценарий

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

Что я привык к

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

Что я сейчас делаю

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

Альтернативы Lackluster

(Примечание. Я ссылаюсь на эти вопросы с помощью stackoverflow:

  • Javafx 2.0 How-Application 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.
  • Шина событий

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

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

Что я ищу

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

4b9b3361

Ответ 1

Обновление

В дополнение к afterburner.fx также checkout Gluon Ignite:

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

Ввод объектов модели в контроллеры также через @Inject, аналогичный afterburner.fx.

Рекомендуемый подход

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

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. Я думаю, вы можете установить контроллер factory на экземпляр FXMLLoader, чтобы инициировать инъекцию. Вот как это делает FXMLView в afterburner.fx. Точная деталь механизма впрыска, используемого в Guice, будет отличаться от механизма afterburner.fx, но я думаю, что широкая концепция установки контроллера factory остается схожей.

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

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

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

Ответ 2

Я изучал эту проблему, и я обнаружил, что инъекция зависимостей вместе с singleton (для модели) - это мой оптимальный дизайн. Я узнал, что использование метода setter для модели в каждом из моих контроллеров является формой инъекции зависимостей, поскольку передача модели через конструктор. 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"/>

Ответ 3

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