Javaのオブザーバーパターン
1. 概要
この記事では、オブザーバーパターンについて説明し、Java実装の代替案をいくつか見ていきます。
2. オブザーバーパターンとは何ですか?
オブザーバーは動作デザインパターンです。 オブジェクト間の通信を指定します:observableとobservers。 An 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で入手できます。