Observer パターン

Observer パターンは振る舞いに関するデザインパターンの一種で、オブジェクト間の一対多の依存関係を定義します。あるオブジェクトの状態が変化すると、そのすべての依存者に通知が行われ、自動的に更新されます。

オブジェクト間に一対多の関係が存在する場合は、Observer パターンを使用します。例えば、あるオブジェクトが変更されると、それに依存するオブジェクトに自動的に通知されます。Observer パターンは振る舞いに関するパターンに属します。

紹介

意図

オブジェクト間に一対多の依存関係を定義し、あるオブジェクトの状態が変化したときに、それに依存するすべてのオブジェクトが通知を受け取り自動的に更新します。

主に解決する問題

  • Observer パターンが解決するのは、あるオブジェクトの状態が変化したときに、他の依存オブジェクトへ自動的に通知する方法の問題であり、同時にオブジェクト間の疎結合と高い協調性を維持します。

使用シーン

  • あるオブジェクトの状態変化に合わせて他のオブジェクトも同時に更新する必要がある場合。

実装方法

  • Observer インターフェースを定義する:更新メソッドを1つ含みます。
  • 具体的な Observer を作成する:Observer インターフェースを実装し、通知を受信したときの動作を定義します。
  • Subject インターフェースを定義する:Observer の追加、削除、通知を行うメソッドを含みます。
  • 具体的な Subject を作成する:Subject インターフェースを実装し、Observer のリストを管理して、状態が変化したときに通知します。

キーコード

  • Observer リスト:Subject 内に Observer のリストを保持します。

応用例

  • オークションシステム:競売人が Subject、入札者が Observer として、オークション価格が更新されたときに入札者全員に通知します。
  • 西遊記の物語:観音様が水をまくことを状態変化とし、老いた亀が Observer としてその変化を観察します。

利点

  • 抽象結合:Observer と Subject は抽象結合されています。
  • トリガーメカニズム:状態変化時のトリガーと通知の仕組みを確立します。

欠点

  • パフォーマンスの問題:Observer が多い場合、通知処理に時間がかかる可能性があります。
  • 循環依存:循環呼び出しやシステムクラッシュを引き起こす可能性があります。
  • 変化の詳細が不足:Observer は Subject がどのように変化したかを知らず、変化が発生したことだけを知ります。

使用上のアドバイス

  • オブジェクト間の結合度を下げる必要があり、オブジェクトの状態変化が他のオブジェクトの変化をトリガーする必要がある場合に使用します。
  • Java に組み込みの Observer パターン対応クラス、例えばjava.util.Observableとjava.util.Observer。

注意事項

  • 循環参照の回避:Observer と Subject の間の依存関係に注意し、循環参照を避けます。
  • 非同期実行:非同期通知を使用して、単一障害点によってシステム全体が停止するのを防ぐことを検討してください。

構造

Observer パターンには以下のような中核となる役割が含まれます:

  • Subject(サブジェクト):被観察者またはオブザーバブルとも呼ばれ、状態を持つオブジェクトであり、Observer のリストを保持します。Subject は Observer の追加、削除、通知のメソッドを提供します。
  • Observer(オブザーバー):Subject からの通知を受け取るオブジェクトです。Observer は更新メソッドを実装する必要があり、Subject から通知を受け取ると、そのメソッドを呼び出して更新処理を行います。
  • Concrete Subject(具体的なサブジェクト):Subject の具体的な実装クラスです。Observer のリストを保持し、状態が変化したときに Observer へ通知します。
  • Concrete Observer(具体的なオブザーバー):Observer の具体的な実装クラスです。更新メソッドを実装し、Subject から通知を受け取ったときに実行する具体的な操作を定義します。

Observer パターンは Subject と Observer を分離することで、オブジェクト間の疎結合を実現します。Subject の状態が変化すると、それに依存するすべての Observer が通知を受け取り、対応する更新を行います。

実装

Observer パターンは Subject、Observer、Client の3つのクラスを使用します。Subject オブジェクトには、Observer を Client オブジェクトにバインドするメソッドと、Client オブジェクトから Observer をバインド解除するメソッドがあります。私たちは作成しますSubjectクラス、Observer抽象クラスと、その抽象クラスを拡張したObserver具体的なクラス。

ObserverPatternDemo、私たちのデモクラスはSubjectと具体的なクラスのオブジェクトを使用して Observer パターンをデモします。

观察者模式的 UML 图

ステップ 1

Subject クラスを作成します。

Subject.java

import java.util.ArrayList; import java.util.List; public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } public void notifyAllObservers(){ for (Observer observer : observers) { observer.update(); } } }

ステップ 2

Observer クラスを作成します。

Observer.java

public abstract class Observer { protected Subject subject; public abstract void update(); }

ステップ 3

具体的な Observer クラスを作成します。

BinaryObserver.java

public class BinaryObserver extends Observer{ public BinaryObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); } }

OctalObserver.java

public class OctalObserver extends Observer{ public OctalObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); } }

HexaObserver.java

public class HexaObserver extends Observer{ public HexaObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); } }

ステップ 4

使用するSubjectと具体的な Observer オブジェクト。

ObserverPatternDemo.java

public class ObserverPatternDemo { public static void main(String[] args) { Subject subject = new Subject(); new HexaObserver(subject); new OctalObserver(subject); new BinaryObserver(subject); System.out.println("First state change: 15"); subject.setState(15); System.out.println("Second state change: 10"); subject.setState(10); } }

ステップ 5

プログラムを実行し、結果を出力します:

First state change: 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010
その他の拡張