Observerパターン
変化するデータや状態に関心を持つオブザーバ(観察者:Observer)と、その変化を通知するサブジェクト(観察対象:Subject)から構成される。
一対多の依存関係を定義し、あるオブジェクト(サブジェクト)の状態が変化すると、それに依存する複数の他のオブジェクト(オブザーバ)に自動的に通知される。
サブジェクト(Subject)
観察(Observe)の対象となる情報。
状態が変化する。
観察対象となるデータの唯一の所持者であり、観察対象に何らかの変更があった場合、オブザーバに対してその変更を通知する。
また、オブザーバを管理する(オブザーバの新規登録や、オブザーバの解除)役割も併せ持つ。
サブジェクトがオブザーバについて知っているのは、Observer
インターフェースを実装しているということだけ。通知した情報がどのように利用されるのかなどは一切知らない(疎結合の関係)。
<<学習memo:発行者(Publisher)が管理している対象をサブジェクト(Subject)と言っているのかと思っていましたが、サブジェクト自身が発行者であるという理解の方が、定義的には正しいのかもしれません。または、Publish-Subscribeパターンというものも存在し、Publish-Subscribeパターンでは発行者が、この記事でいうサブジェクトの役割を果たすようです。>>
オブザーバ(Observer)・リスナー(Listener)
対象となる情報を観察(Observe)する主体。
購読者、サブスクライバ、リスナーとも呼ばれる。
サブジェクトの更新通知を受信する。
観察情報の購読(サブスクライブ)は、Subject
が提供するregistObserver()
によって行う。観察対象を購読することで、オブザーバになることができる。
反対に、removeObserver()
によって、途中で購読を解除して、オブザーバを辞めることもできる。
必要なインターフェース
public interface Subject {
public void registObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
public interface Observer {
public void doSomething(int data);
}
インターフェースの実装
import java.util.ArrayList;
import java.util.List;
public class SubjectSample implements Subject {
// サブジェクトはオブザーバを保持している
private List<Observer> observers;
// 観察対象の情報
private int data = 0;
public SubjectSample() {
observers = new ArrayList<Observer>();
}
// 購読を開始する
@Override
public void registObserver(Observer o) {
observers.add(o);
}
// 購読を解除する
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
// 観察対象に変更があったことをオブザーバに対して通知する
@Override
public void notifyObservers() {
for (Observer observer : observers) {
// 引数に更新データを渡しても良いし、
// getData()などを用意して、更新があったことだけを通知し、オブザーバ側から取得させても良い。
observer.doSomething(data);
}
}
// 観察対象のデータが変更される処理
public void updateData(int newData) {
data = newData;
notifyObservers();
}
}
public class ObserverSample implements Observer{
// サブジェクトへの参照を保持しておくと、あとで解除も行うことができる
private SubjectSample subject;
public ObserverSample(SubjectSample subject){
this.subject = subject;
// 購読を開始する
subject.registObserver(this);
}
// 観察対象に更新があった場合、サブジェクト側で呼び出されるメソッド
@Override
public void doSomething(int data) {
System.out.println("情報が更新されたため、何らかの処理を実行します。");
System.out.println("最新のデータは" + data + "です。");
}
}
public class Main {
public static void main(String[] args) {
SubjectSample subject = new SubjectSample();
ObserverSample observer = new ObserverSample(subject);
// 観察対象の情報を変更する → サブジェクトが変更を通知 → オブザーバが通知を受信
subject.updateData(100);
// >>情報が更新されたため、何らかの処理を実行します。
// >>最新のデータは100です。
}
}