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