「デザインパターン」オブザーバーパターン
定義:
- パブリッシュ/サブスクライブモードとも呼ばれるオブザーバー モードは、 1 対多の依存関係を定義し、サブジェクト オブジェクトは複数のオブザーバー オブジェクトによって同時に観察 (監視) できます。サブジェクト オブジェクトの状態が変化すると、すべてのオブザーバー オブジェクトに通知され、自動的に更新されます。
- オブザーバー モードの核心は、オブザーバーを監視対象から切り離し、パブリッシュ/サブスクライブと同様の方法で 2 つを関連付けることです。これにより、監視対象のステータス更新を、関心のあるオブザーバーに通知できます。
オブザーバー パターンの構成上の役割:
- Subject (Subject) : 抽象通知機能とも呼ばれ、通常はインターフェースまたは抽象クラスによって実装されます。すべてのオブザーバー オブジェクトへの参照のコレクションを保持し、オブザーバー オブジェクトを追加および削除するためのメソッドと、すべてのオブザーバー オブジェクトに通知するためのメソッドを定義します。
- ConcreteSubject : 特定の通知機能とも呼ばれ、状態 (データ) が変化すると、すべてのオブザーバーに通知を送信します。
- 抽象オブザーバー (オブザーバー) : 通常、抽象クラスまたはインターフェイスによって実装され、すべての特定のオブザーバーのインターフェイスを定義し、サブジェクトから通知されるとそれ自体を更新し、通常は
update()
メソッドを - 具体的なオブザーバー (ConcreteObserver) : 抽象的なオブザーバーの更新インターフェイスを実装し、同時に具体的なオブザーバーで特定のサブジェクト オブジェクトへの参照を維持し、特定のオブザーバーの関連する状態を保存します。これらの状態は、特定の対象オブジェクトの状態。
オブザーバー パターンの UML クラス図:
非常に単純なアプリケーション シナリオのケースを取り上げます。ニュース公式アカウントをシミュレートし、ユーザーは公式アカウントを自由に購読および購読解除でき、ニュース公式アカウントにニュース リリースがある場合、公式アカウントを購読しているすべてのユーザーに通知されます。いつでも、すべてのサブスクライバーは同じニュース更新を受け取ります。
抽象的な件名 件名:
public interface Subject {
/**
* 增加观察者对象
* @param observer
*/
void attach(Observer observer);
/**
* 删除观察者对象
* @param observer
*/
void detach(Observer observer);
/**
* 通知所有观察者对象
*/
void notifyObserver();
}
特定の主題 ConcreteSubject :
public class ConcreteSubject implements Subject{
private List<Observer> observers = new ArrayList<>();
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
アブストラクト オブザーバー オブザーバー:
public interface Observer {
/**
* 更新观察者的状态
*/
void update();
}
具体的なオブザーバー ConcreteObserver :
public class ConcreteObserver implements Observer{
private String name;
private String observerState;
private ConcreteSubject subject;
public ConcreteObserver(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
}
@Override
public void update() {
observerState = subject.getSubjectState();
System.out.println(String.format("观察者%s收到的消息是:%s", name, observerState));
}
}
クライアント クライアント:
public class Client {
public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
concreteSubject.attach(new ConcreteObserver("node1", concreteSubject));
concreteSubject.attach(new ConcreteObserver("node2", concreteSubject));
concreteSubject.attach(new ConcreteObserver("node3", concreteSubject));
concreteSubject.setSubjectState("Hello, observers!");
concreteSubject.notifyObserver();
}
}
出力結果:
观察者node1收到的消息是:Hello, observers!
观察者node2收到的消息是:Hello, observers!
观察者node3收到的消息是:Hello, observers!
実際、JDK ソース コードは、オブザーバー モードjava.util
、Observer
インターフェイス (abstract notifier に相当)、およびObservable
クラス(オブザーバー オブジェクトに相当) をパッケージの下ですばやく使用するためのスケルトンを提供します。
Observer 接口
:
/**
* 抽象通知者
*/
public interface Observer {
void update(Observable o, Object arg);
}
Observer 类
:
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
public Observable() {
obs = new Vector<>();
}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
Observer インターフェイスと Observable クラスの UML クラス図を以下に示します。
オブザーバー パターンの利点:
- 観察対象とオブザーバーの間に抽象的な結合を確立する. 観察対象は、特定のオブザーバーを知らずに抽象的なオブザーバーのコレクションを維持するだけでよいため、各変更は依存関係に沿った反対側の変更に影響を与えません。原則として反転。
- ブロードキャスト通信に対応。観測ターゲットは、登録されているすべてのオブザーバ オブジェクトに通知を送信し、1 対多のシステム設計の難しさを簡素化します。
- 開閉原理の要件を満たし、新しい特定のオブザーバーを追加しても、元のシステム コードを変更する必要はありません。また、特定の観測者と観測対象との間に相関関係がない場合に、新たに観測対象を追加することも便利です。
オブザーバー パターンの欠点:
- 観測対象物に直接観測者と間接観測者が多数存在する場合、すべての観測者に通知するまでに多くの時間がかかります。
- オブザーバーと監視対象ターゲットの間に循環依存関係がある場合、監視対象ターゲットはそれらの間の循環呼び出しをトリガーし、システムをクラッシュさせる可能性があります。
- オブザーバーが、観測されたターゲット オブジェクトがどのように変化したかを知るための対応するメカニズムはありませんが、観測されたターゲットが変化したことだけを知ることができます。
オブザーバー モードのアプリケーション シナリオ:
- 抽象モデルには、一方が他方に依存する 2 つの側面がありますが、このとき、オブザーバー パターンを使用して、この 2 つを独立したオブジェクトにカプセル化し、個別に変更して再利用できるようにすることができます。
- 1 つのオブジェクトを変更すると、1 つまたは複数の他のオブジェクトが変更されますが、変更されるオブジェクトの数やそれらのオブジェクトが誰であるかは正確にはわかりません。
- システムにトリガー チェーンを作成する必要があります。A オブジェクトの動作は B オブジェクトに影響し、B オブジェクトの動作は C オブジェクトに影響します。オブザーバー パターンを使用してチェーン トリガー メカニズムを作成できます。