Рубрики
Без рубрики

Шаблон наблюдателя в Java

Узнайте несколько способов реализации шаблона проектирования Observer в Java

Автор оригинала: Predrag.

1. Обзор

В этой статье мы опишем шаблон Observer и рассмотрим несколько вариантов реализации Java.

2. Что такое Паттерн Наблюдателя?

Наблюдатель-это поведенческий шаблон проектирования. Он определяет связь между объектами: наблюдаемыми и наблюдателями . An observable – это объект, который уведомляет наблюдателей об изменениях в своем состоянии.

Например, новостное агентство может уведомлять каналы о получении новостей. Получение новостей-это то, что изменяет состояние информационного агентства, и это заставляет каналы получать уведомления.

Давайте посмотрим, как мы можем реализовать его сами.

Во-первых, давайте определим класс Информационное агентство :

public class NewsAgency {
    private String news;
    private List channels = 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 .