banner
jzman

jzman

Coding、思考、自觉。
github

観察者デザインパターン

最近、デザインパターンに関連する知識を補完しています。観察者デザインパターンについては、以下の項目を中心に学習しています。具体的には以下の通りです:

  1. 観察者デザインパターンとは何か
  2. キーワードの理解
  3. 観察者への通知方法
  4. 観察者パターンの実装
  5. 観察者パターンの利点と欠点
  6. 使用シーン

観察者デザインパターンとは何か#

観察者パターン(Observer)は、ソフトウェアデザインパターンの一種で、オブジェクト間の一対多の関係を定義します。つまり、オブジェクトのデータが変化した場合、それに依存する他のオブジェクトに通知し、データの変化に応じて反応する関係です。このような、ターゲットオブジェクトのデータが変化すると、それに対応する観察者オブジェクトのデータも変化する、一対多の通知関係を持つデザインパターンを観察者デザインパターンと呼びます。

キーワードの理解#

観察者デザインパターンでは、主に 2 つの概念を区別します:

  • 観察者:観察者オブジェクトを指し、メッセージの購読者です。
  • 被観察者:観察する対象オブジェクトを指し、メッセージの発行者です。

観察者への通知方法#

被観察者のデータが変化した場合、主に 2 つの方法で観察者に通知します。具体的には以下の通りです:

  • プッシュ:メッセージをブロードキャストのように通知し、観察者は受動的かつ無条件で受け取ります。
  • プル被観察者からの通知を受け取った後、メッセージの取得を自由に決定します。

観察者パターンの実装#

以下の 2 つの方法で観察者デザインパターンを実装します。具体的には以下の通りです:

  1. 手動で観察者デザインパターンを実装する
  2. Java API で提供される観察者デザインパターンを使用する(Java API の Observer と Observable を使用して観察者パターンを実装する)

手動で観察者パターンを実装する#

まず、被観察者を作成します。具体的な実装は以下の通りです:

/**
 * 観察者が観察する対象オブジェクト
 */
public abstract class Subject {
	protected ArrayList<Observer> observerList = new ArrayList<>();
	// 観察者が対象オブジェクト(被観察者)を観察し始めることを示す
	public void registerObserver(Observer obs) {
		observerList.add(obs);
	}
	// 観察者が対象オブジェクト(被観察者)の観察を解除することを示す
	public void unRegisterObserver(Observer obs) {
		observerList.remove(obs);
	}
	// 対象オブジェクト(被観察者)の状態が変化した場合、観察者の状態を即座に更新する
	public void notifyAllObserver(){
		for (Observer observer : observerList) {
			observer.update(this);
		}
	}
}

具体的な被観察者を作成します。具体的な実装は以下の通りです:

/**
 * 具体的な対象オブジェクト(被観察者)
 */
public class ConcreteSubject extends Subject{
	private int state;
	public int getState() {
		return state;
	}
	public void setState(int state) {
		this.state = state;
		// データが変化した場合、他の観察者に通知する
		notifyAllObserver();
	}
}

次に、統一的かつ便利な方法を提供するために、観察者インターフェースを定義します。具体的な実装は以下の通りです:

/**
 * 観察者の統一的なインターフェース
 */
public interface Observer {
	void update(Subject subject);
}

次に、具体的な観察者を作成します。具体的な実装は以下の通りです:

/**
 * 具体的な観察者
 */
public class ConcreteObserver implements Observer{
	private int state;
	public int getState() {
		return state;
	}
	public void setState(int state) {
		this.state = state;
	}
	@Override
	public void update(Subject subject) {
		// 対象オブジェクトのデータ変化を取得し、現在の観察者を更新する
		ConcreteSubject concreteSubject = (ConcreteSubject)subject;
		state = concreteSubject.getState();
	}
}

最後に、観察者デザインパターンをテストします。具体的な実装は以下の通りです:

/**
 * メイン
 */
public class Client {
	public static void main(String[] args) {
		// 具体的な対象オブジェクト(被観察者)を作成する
		ConcreteSubject concreteSubject = new ConcreteSubject();
		// 複数の具体的な観察者を作成する
		ConcreteObserver obs1 = new ConcreteObserver();
		ConcreteObserver obs2 = new ConcreteObserver();
		ConcreteObserver obs3 = new ConcreteObserver();
		// 観察者が対象オブジェクト(被観察者)のデータ変化を観察するようにする
		concreteSubject.observerList.add(obs1);
		concreteSubject.observerList.add(obs2);
		concreteSubject.observerList.add(obs3);
		// 特定の対象オブジェクト(被観察者)のデータを変更する
		concreteSubject.setState(10);
		// 観察者のデータが対象オブジェクトのデータ変化と一致しているかどうかを確認する
		System.out.println("観察者obs1:" + obs1.getState());
		System.out.println("観察者obs2:" + obs2.getState());
		System.out.println("観察者obs3:" + obs3.getState());
	}
}

明らかに、実行結果は以下の通りです:

観察者obs1:10
観察者obs2:10
観察者obs3:10

対象オブジェクトのデータの変更により、それに対応する観察者のデータが更新され、メッセージの購読と送信が実現されます。

Java API で提供される観察者デザインパターン#

Java API で提供される観察者デザインパターンは、Observer と Observable を使用して実装されます。まず、被観察者として Observer を継承したクラスを作成します。具体的な実装は以下の通りです:

/**
 * 被観察者(対象オブジェクト)
 */
public class ConcreteSubject extends Observable{
	private int state;
	public int getState() {
		return state;
	}
	public void setState(int state) {
		this.state = state;
		// データが変化したことを示す
		setChanged();
		// 具体的な対象オブジェクトのデータ変化を通知する
		notifyObservers(state);
	}
}

次に、Observer を継承したクラスを作成し、観察者として使用します。具体的な実装は以下の通りです:

/**
 * 観察者
 */
public class ConcreteObserver implements Observer{
	private int state;
	public int getState() {
		return state;
	}
	public void setState(int state) {
		this.state = state;
	}
	@Override
	public void update(Observable arg0, Object arg1) {
		ConcreteSubject concreteSubject = (ConcreteSubject) arg0;
		// 対象オブジェクト(被観察者)のデータ変化に基づいて、現在の観察者のデータを更新する
		this.state = concreteSubject.getState();
	}
}

最後に、観察者デザインパターンをテストします。具体的な実装は以下の通りです:

/**
 * 観察者デザインパターンのテスト
 */
public class Client {
	public static void main(String[] args) {
		ConcreteSubject concreteSubject = new ConcreteSubject();
		
		ConcreteObserver obs1 = new ConcreteObserver();
		ConcreteObserver obs2 = new ConcreteObserver();
		ConcreteObserver obs3 = new ConcreteObserver();
		
		concreteSubject.addObserver(obs1);
		concreteSubject.addObserver(obs2);
		concreteSubject.addObserver(obs3);
		
		concreteSubject.setState(100);
		
		System.out.println("観察者obs1:" + obs1.getState());
		System.out.println("観察者obs2:" + obs2.getState());
		System.out.println("観察者obs3:" + obs3.getState());
	}
}

明らかに、実行結果は以下の通りです:

観察者obs1:100
観察者obs2:100
観察者obs3:100

観察者パターンの利点と欠点#

  • 利点:観察者と被観察者の抽象化により、安定したメッセージトリガーメカニズムを定義できます。
  • 欠点:被観察者に複数の間接的な観察者がある場合、メッセージの伝達により時間がかかる可能性があります。また、観察者被観察者の間に循環依存関係がある場合、システムのクラッシュが発生する可能性があります。

使用シーン#

観察者デザインパターンは、開発のさまざまな場面で広く使用されます。具体的な使用シーンは以下の通りです:

  • ゲームやチャットなどのプロセスで、サーバーからクライアントへのメッセージの転送プロセス
  • Android のブロードキャストメカニズムや、ListView でデータが変更されたことを通知する場合など
  • 購読関連のシステム、例えば購読しているトピックが更新されると、購読者は同期して記事を購読する。

観察者デザインパターンは以上です。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。