banner
jzman

jzman

Coding、思考、自觉。
github

オブザーバー設計パターン

最近デザインパターンに関する知識を補充しています。オブザーバーデザインパターンについては、主に以下のいくつかの側面から学びます。具体的には次の通りです:

  1. オブザーバーデザインパターンとは
  2. 重要な概念の理解
  3. オブザーバーへの通知方法
  4. オブザーバーパターンの実装
  5. オブザーバーパターンの利点と欠点
  6. 使用シーン

オブザーバーデザインパターンとは#

オブザーバーパターン(Observer)は、ソフトウェアデザインパターンの一つで、オブジェクト間の一対多の関係を定義します。つまり、あるオブジェクトのデータが変化すると、それに依存する他のオブジェクトに通知され、そのデータの変化に応じて動作します。このように、対象オブジェクトのデータが変化すると、それに対応するオブザーバーオブジェクトのデータも変化する、一対多の通知関係を持つデザインパターンをオブザーバーデザインパターンと呼びます。

重要な概念の理解#

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

  • オブザーバー:オブザーバーオブジェクト、つまりメッセージの購読者
  • 被観察者:観察対象のオブジェクト、つまりメッセージの発行者

オブザーバーへの通知方法#

被観察者のデータが変化したとき、主に 2 つの方法でオブザーバーに通知します。具体的には次の通りです:

  • プッシュ:メッセージがブロードキャストのようにオブザーバーに通知され、オブザーバーは受動的かつ無条件に受け取ります;
  • プル被観察者からの通知を受け取った後、自主的にメッセージを取得することができます。

オブザーバーパターンの実装#

以下に、オブザーバーデザインパターンの実装を 2 つの方法で示します。具体的には次の通りです:

  1. 手書きのオブザーバーデザインパターン
  2. Java API が提供するオブザーバーデザインパターンを使用して、Java API が提供する Observer と Observable を使用してオブザーバーパターンを実装します。

手書きのオブザーバーパターン#

まず、被観察者を作成します。具体的には次の通りです:

/**
 * オブザーバーが観察する対象オブジェクト
 * @author jzman
 */
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);
		}
	}
}

具体的な被観察者を作成します。具体的には次の通りです:

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

次に、統一のためにオブザーバーインターフェースを定義します。具体的には次の通りです:

/**
 * オブザーバー統一インターフェース
 * @author jzman
 */
public interface Observer {
	void update(Subject subject);
}

次に、具体的なオブザーバーを作成します。具体的には次の通りです:

/**
 * 具体的なオブザーバー
 * @author jzman
 */
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();
	}
}

最後に、オブザーバーデザインパターンをテストします。具体的には次の通りです:

/**
 * メイン
 * @author jzman
 */
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 を使用して実現されます。まず、Observable を継承したクラスを作成し、被観察者とします。具体的には次の通りです:

/**
 * 被観察者(対象オブジェクト)
 * @author jzman
 */
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 を継承したクラスを作成し、オブザーバーとします。具体的には次の通りです:

/**
 * オブザーバー
 * @author jzman
 */
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();
	}
}

最後に、オブザーバーデザインパターンをテストします。具体的には次の通りです:

/**
 * オブザーバーデザインパターンのテスト
 * @author jzman
 */
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 でデータが変化したときの通知もオブザーバーデザインパターンです
  • 購読関連のシステム、例えば、購読しているテーマに更新があった場合、購読者は同期して購読している記事を受け取ります。

オブザーバーデザインパターンはこれで終了です。

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