Javafx и шаблон Observer — обновление пользовательского интерфейса

Я пытаюсь реализовать шаблон Observer в приложении JavaFx. Я никогда не задавал здесь вопросов, но это сводит меня с ума.

По сути, я пытаюсь использовать шаблон Observer для мониторинга класса, который анализирует файл телефонных номеров, и автоматически обновляет пользовательский интерфейс при анализе файла.

Прежде чем я перейду к своим вопросам, вот мой код:

Абстрактный класс Observer.java

public abstract class Observer 
{
   public PhoneBook numbers;

   public abstract void update();
}

У меня есть класс, который реализует это:

public class PhoneBookObserver extends Observer {

    public PhoneBookObserver(PhoneBook numbers)
    {
        this.numbers = numbers;
        this.numbers.attach(this);
    }

    @Override
    public void update()
    {
        System.out.println(""NUMBER - : " + numbers.GetNumbers());

    }
}

В классе, выполняющем синтаксический анализ, я создал новый PhoneBookObserver.

public PhoneBook ParsePhoneBook() 
{   
    PhoneBook nums= new PhoneBook();
    PhoneBookObserver p = new PhoneBookObserver(nums);

    // ... Parsing of file - works fine

   return nums;
}

В настоящее время это работает, и мой println из update() в PhoneBookObserver выводится.

Мои вопросы:

  • Может ли метод обновления PhoneBookObserver обновить мой пользовательский интерфейс? Как он получит доступ к элементам JavaFx в моем контроллере?
  • Могу ли я просто сделать свой контроллер наблюдателем, переопределить update() и использовать его для обновления элементов пользовательского интерфейса из моего контроллера? Это плохо?

person timbo baggins    schedule 02.03.2017    source источник
comment
Небольшое отступление: Свойства JavaFX По сути, уже реализовали для вас шаблон наблюдателя. Почему бы просто не использовать их и не изобретать велосипед?   -  person James_D    schedule 03.03.2017
comment
Куда добавить слушателей? В инициализации?   -  person timbo baggins    schedule 03.03.2017
comment
Обычно да. Это зависит от того, как вы все настроите, но это будет очевидный выбор в большинстве сценариев. (Слушатель — это просто наблюдаемое в терминологии шаблона наблюдателя. Свойства JavaFX или наблюдаемые списки — это наблюдаемые.)   -  person James_D    schedule 03.03.2017
comment
Что, если я хочу обновить свой пользовательский интерфейс с помощью update() в PhoneBookObserver, возможно ли это?   -  person timbo baggins    schedule 03.03.2017
comment
Я бы либо реализовал PhoneBookObserver как внутренний класс в контроллере, либо (эквивалентно) предоставил реализацию верхнего уровня, которая имела бы ссылку на контроллер. TBH, я бы, вероятно, удалил поле numbers из Observer - на самом деле оно не служит никакой цели, поскольку update() на него не ссылается. Затем вы можете сделать Observer интерфейс. И тогда можно просто передать лямбда-выражение в PhoneBook.attach(...), и теперь тривиально создать реализацию прямо в контроллере...   -  person James_D    schedule 03.03.2017
comment
Можете ли вы привести пример того, как я могу использовать метод внутреннего класса?   -  person timbo baggins    schedule 03.03.2017


Ответы (1)


Чтобы напрямую ответить на ваш вопрос, я бы, вероятно, реализовал Observer как внутренний класс в контроллере. Тогда он имеет доступ ко всему в контроллере.

Предполагая здесь, что PhoneBook определяет метод формы

public List<PhoneNumber> getPhoneNumbers() ;

тогда вы можете сделать:

public class Controller {

    @FXML
    private ListView<PhoneNumber> phoneNumberList ;

    private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere

    public void initialize() {
        numbers.attach(new PhoneBookObserver(numbers));
        // ...
    }

    private class PhoneBookObserver extends Observer {

        PhoneBookObserver(PhoneBook numbers) {
            this.numbers = numbers ;
        }

        @Override
        public void update() {
            phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
        }
    }
}

Обратите внимание, что в

public abstract class Observer 
{
   public PhoneBook numbers;

   public abstract void update();
}

поле numbers действительно бесполезно, так как единственный метод его не использует. Таким образом, вы можете удалить его (подклассы могут определить такое поле, если им нужно). Тогда вы можете также сделать его интерфейсом, и, поскольку у него есть только один метод, это @FunctionalInterface:

@FunctionalInterface
public interface Observer {
    public void update() ;
}

и теперь его можно реализовать с помощью лямбда-выражения, поэтому реализация настолько тонкая, что у вас практически не возникает проблем с «доступом к пользовательскому интерфейсу»:

public class Controller {

    @FXML
    private ListView<PhoneNumber> phoneNumberList ;

    private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere

    public void initialize() {
        numbers.attach(() -> phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
        // ...
    }

}

Наконец, обратите внимание, что Свойства JavaFX и наблюдаемые списки в основном уже обеспечивают реализацию шаблона наблюдателя, так что здесь вы в значительной степени заново изобретаете колесо. Вы могли бы просто иметь

public class PhoneBook {

    private final ObservableList<PhoneNumber> numbers;

    public ObservableList<PhoneNumber> getPhoneNumbers() {
        return numbers ;
    }
}

а потом

public class Controller {

    @FXML
    private ListView<PhoneNumber> phoneNumberList ;

    private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere

    public void initialize() {
        phoneNumberList.setItems(numbers.getPhoneNumbers());
    }

}

и представление списка будет наблюдать за (уже наблюдаемым) списком чисел для вас. Нет никакой реальной необходимости в ваших Observer или PhoneBookObserver.

person James_D    schedule 02.03.2017
comment
Попробую, но в методе инициализации мне не нужно будет передавать объект PhoneBook в numbers.attach(new PhoneBookObserver());? - person timbo baggins; 03.03.2017
comment
Да, извините, исправил. (Я уже думал, что это было излишним :).) - person James_D; 03.03.2017
comment
Я пробовал это, и метод update() в PhoneBookObserver никогда не срабатывает, я также добавил println, который не выводится. - person timbo baggins; 03.03.2017
comment
Я предполагаю, что парсер имеет тот же экземпляр PhoneBook, который использует контроллер? - person James_D; 03.03.2017
comment
Решил, спасибо. Собираюсь отметить это как ответ. - person timbo baggins; 03.03.2017