Javaにおけるオブザーバパターン

Javaのオブザーバーパターン

1. 概要

この記事では、オブザーバーパターンについて説明し、Java実装の代替案をいくつか見ていきます。

2. オブザーバーパターンとは何ですか?

オブザーバーは動作デザインパターンです。 オブジェクト間の通信を指定します:observableobserversAn observable is an object which notifies observers about the changes in its state.

たとえば、通信社はニュースを受信したときにチャネルに通知できます。 ニュースの受信は、通信社の状態を変更するものであり、チャンネルに通知されます。

自分たちでそれを実装する方法を見てみましょう。

まず、NewsAgencyクラスを定義しましょう。

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);
        }
    }
}

NewsAgencyは監視可能であり、newsが更新されると、NewsAgencyの状態が変化します。 変更が発生すると、NewsAgencyは、update()メソッドを呼び出すことにより、この事実をオブザーバーに通知します。

To be able to do that, the observable object needs to keep references to the observers、この場合はchannels変数です。

ここで、オブザーバー,Channelクラスがどのように見えるかを見てみましょう。 NewsAgencyの状態が変化したときに呼び出されるupdate()メソッドが必要です。

public class NewsChannel implements Channel {
    private String news;

    @Override
    public void update(Object news) {
        this.setNews((String) news);
    }
}

Channelインターフェースには、次の1つのメソッドしかありません。

public interface Channel {
    public void update(Object o);
}

ここで、NewsChannelのインスタンスをオブザーバー,のリストに追加し、NewsAgencyの状態を変更すると、NewsChannelのインスタンスが更新されます。

NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Javaコアライブラリには事前定義されたObserverインターフェースがあり、オブザーバーパターンの実装がさらに簡単になります。 それを見てみましょう。

3. Observerを使用した実装

java.util.Observerインターフェースはupdate()メソッドを定義するため、前のセクションで行ったように自分で定義する必要はありません。

実装でどのように使用できるか見てみましょう。

public class ONewsChannel implements Observer {

    private String news;

    @Override
    public void update(Observable o, Object news) {
        this.setNews((String) news);
    }
}

ここで、2番目の引数は、以下に示すようにObservableから取得されます。

observable,を定義するには、JavaのObservableクラスを拡張する必要があります。

public class ONewsAgency extends Observable {
    private String news;

    public void setNews(String news) {
        this.news = news;
        setChanged();
        notifyObservers(news);
    }
}

オブザーバーのupdate()メソッドを直接呼び出す必要はないことに注意してください。 stateChanged()notifyObservers()を呼び出すだけで、残りはObservableクラスが実行します。

また、オブザーバーのリストが含まれ、そのリストを維持するためのメソッドが公開されています–addObserver()およびdeleteObserver().

結果をテストするには、このリストにオブザーバーを追加し、ニュースを設定するだけです。

ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Observerインターフェースは完全ではなく、Java9以降非推奨になっています。 その短所の1つは、Observableがインターフェイスではなくクラスであるということです。そのため、サブクラスをオブザーバブルとして使用することはできません。

また、開発者はObservableの同期メソッドの一部をオーバーライドして、スレッドセーフを破壊する可能性があります。

Observerを使用する代わりに推奨されるProperyChangeListenerインターフェースを見てみましょう。

4. PropertyChangeListenerを使用した実装

In this implementation, an observable must keep a reference to the PropertyChangeSupport instance.クラスのプロパティが変更されたときにオブザーバーに通知を送信すると便利です。

オブザーバブルを定義しましょう:

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を使用して、オブザーバーを追加および削除し、オブザーバブルの状態が変化したときにそれらに通知できます。

support.firePropertyChange("news", this.news, value);

ここで、最初の引数は、監視されるプロパティの名前です。 2番目と3番目の引数は、それに応じて古い値と新しい値です。

オブザーバーは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. 結論

この記事では、JavaでObserverデザインパターンを実装する2つの方法を検討しましたが、PropertyChangeListenerアプローチが推奨されます。

記事のソースコードはover on GitHubで入手できます。