最近、デザインパターンに関連する知識を補完しています。観察者デザインパターンについては、以下の項目を中心に学習しています。具体的には以下の通りです:
- 観察者デザインパターンとは何か
- キーワードの理解
- 観察者への通知方法
- 観察者パターンの実装
- 観察者パターンの利点と欠点
- 使用シーン
観察者デザインパターンとは何か#
観察者パターン(Observer)は、ソフトウェアデザインパターンの一種で、オブジェクト間の一対多の関係を定義します。つまり、オブジェクトのデータが変化した場合、それに依存する他のオブジェクトに通知し、データの変化に応じて反応する関係です。このような、ターゲットオブジェクトのデータが変化すると、それに対応する観察者オブジェクトのデータも変化する、一対多の通知関係を持つデザインパターンを観察者デザインパターンと呼びます。
キーワードの理解#
観察者デザインパターンでは、主に 2 つの概念を区別します:
- 観察者:観察者オブジェクトを指し、メッセージの購読者です。
- 被観察者:観察する対象オブジェクトを指し、メッセージの発行者です。
観察者への通知方法#
被被観察者のデータが変化した場合、主に 2 つの方法で観察者に通知します。具体的には以下の通りです:
- プッシュ:メッセージをブロードキャストのように通知し、観察者は受動的かつ無条件で受け取ります。
- プル:被観察者からの通知を受け取った後、メッセージの取得を自由に決定します。
観察者パターンの実装#
以下の 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 でデータが変更されたことを通知する場合など
- 購読関連のシステム、例えば購読しているトピックが更新されると、購読者は同期して記事を購読する。
観察者デザインパターンは以上です。