1. Обзор
В этой статье мы опишем шаблон Observer и рассмотрим несколько вариантов реализации Java.
2. Что такое Паттерн Наблюдателя?
Наблюдатель-это поведенческий шаблон проектирования. Он определяет связь между объектами: наблюдаемыми и наблюдателями . An observable – это объект, который уведомляет наблюдателей об изменениях в своем состоянии.
Например, новостное агентство может уведомлять каналы о получении новостей. Получение новостей-это то, что изменяет состояние информационного агентства, и это заставляет каналы получать уведомления.
Давайте посмотрим, как мы можем реализовать его сами.
Во-первых, давайте определим класс Информационное агентство :
public class NewsAgency { private String news; private Listchannels = new ArrayList<>(); public void addObserver(Channel channel) { this.channels.add(channel); } public void removeObserver(Channel channel) { this.channels.remove(channel); } public void setNews(String news) { this.news = news; for (Channel channel : this.channels) { channel.update(this.news); } } }
Информационное агентство является наблюдаемым, и когда новости обновляются, состояние Информационного агентства меняется. Когда происходит изменение, Информационное агентство уведомляет наблюдателей об этом факте, вызывая их метод update () .
Чтобы сделать это, наблюдаемый объект должен сохранять ссылки на наблюдателей , и в нашем случае это переменная channels .
Давайте теперь посмотрим, как может выглядеть наблюдатель , Канал класс. Он должен иметь метод update() , который вызывается при изменении состояния Информационного агентства :
public class NewsChannel implements Channel { private String news; @Override public void update(Object news) { this.setNews((String) news); } }
Интерфейс Channel имеет только один метод:
public interface Channel { public void update(Object o); }
Теперь, если мы добавим экземпляр News Channel в список наблюдателей , и изменим состояние News Agency , экземпляр NewsChannel будет обновлен:
NewsAgency observable = new NewsAgency(); NewsChannel observer = new NewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");
В основных библиотеках Java есть предопределенный интерфейс Observer , который делает реализацию шаблона observer еще проще. Давайте посмотрим.
3. Реализация С Наблюдателем
Файл java.util.Observer interface определяет метод update () , поэтому нет необходимости определять его самостоятельно, как мы это делали в предыдущем разделе.
Давайте посмотрим, как мы можем использовать его в нашей реализации:
public class ONewsChannel implements Observer { private String news; @Override public void update(Observable o, Object news) { this.setNews((String) news); } }
Здесь второй аргумент исходит из Observable , как мы увидим ниже.
Чтобы определить observable , нам нужно расширить класс Java Observable :
public class ONewsAgency extends Observable { private String news; public void setNews(String news) { this.news = news; setChanged(); notifyObservers(news); } }
Обратите внимание, что нам не нужно напрямую вызывать метод наблюдателя update () . Мы просто вызываем setChanged() и notifyObservers() , а класс Observable делает все остальное за нас.
Кроме того, он содержит список наблюдателей и предоставляет методы для поддержания этого списка – addObserver() и deleteObserver().
Чтобы проверить результат, нам просто нужно добавить наблюдателя в этот список и установить новости:
ONewsAgency observable = new ONewsAgency(); ONewsChannel observer = new ONewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");
Интерфейс Observer не идеален и устарел с Java 9. Один из его минусов заключается в том, что Observable – это не интерфейс, а класс, поэтому подклассы не могут использоваться в качестве наблюдаемых.
Кроме того, разработчик может переопределить некоторые из синхронизированных методов Observable и нарушить их потокобезопасность.
Давайте посмотрим на интерфейс Propertychangelistener , который рекомендуется использовать вместо Observer .
4. Реализация С Помощью PropertyChangeListener
В этой реализации наблюдаемый объект должен содержать ссылку на экземпляр PropertyChangeSupport . Это помогает отправлять уведомления наблюдателям при изменении свойства класса.
Давайте определим наблюдаемое:
public class PCLNewsAgency { private String news; private PropertyChangeSupport support; public PCLNewsAgency() { support = new PropertyChangeSupport(this); } public void addPropertyChangeListener(PropertyChangeListener pcl) { support.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { support.removePropertyChangeListener(pcl); } public void setNews(String value) { support.firePropertyChange("news", this.news, value); this.news = value; } }
Используя эту поддержку , мы можем добавлять и удалять наблюдателей, а также уведомлять их, когда состояние наблюдаемого меняется:
support.firePropertyChange("news", this.news, value);
Здесь первым аргументом является имя наблюдаемого свойства. Второй и третий аргументы-это его старое и новое значение соответственно.
Наблюдатели должны реализовать |/PropertyChangeListener :
public class PCLNewsChannel implements PropertyChangeListener { private String news; public void propertyChange(PropertyChangeEvent evt) { this.setNews((String) evt.getNewValue()); } }
Благодаря классу PropertyChangeSupport , который выполняет проводку для нас, мы можем восстановить новое значение свойства из события.
Давайте протестируем реализацию, чтобы убедиться, что она тоже работает:
PCLNewsAgency observable = new PCLNewsAgency(); PCLNewsChannel observer = new PCLNewsChannel(); observable.addPropertyChangeListener(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");
5. Заключение
В этой статье мы рассмотрели два способа реализации шаблона проектирования Observer в Java, причем предпочтительным является подход PropertyChangeListener .
Исходный код статьи доступен на GitHub .