no_mkd
一.オブザーバーパターンとは?
現実世界の新聞と購読者の関係を抽象化したものがオブザーバーパターンです。一種類の新聞が複数の購読者に対応し、購読者はいつでも購読を解除でき、未購読の読者もいつでも購読を開始できます。新しい新聞が発行されると、すべての購読者が新しい内容を受け取ります。
オブザーバーパターンでは、新聞を主題 Subject と呼び、購読者を観察者 Observer と呼びます。一つの Subject が複数の Observer によって監視され、Observer はいつでも監視を解除でき、新しい Observer もいつでも Subject を監視できます。Subject の内容が変更されると、すべての Observer に通知されます。
二.例を挙げる
多くのオンラインゲームにはクイズイベントがあり、クイズイベントに参加するすべてのプレイヤーが同時に問題情報を受け取ります(遅延は無視)。イベントに参加していないプレイヤーは途中から参加でき、回答中のプレイヤーもいつでも退出できます。この例では、ゲームサーバーが「一」で、プレイヤーが「多」であり、問題情報がそれらの間で伝達されるメッセージです。以上の要件を満たすクラスをどのように設計すればよいでしょうか?オブザーバーパターンを試してみましょう。
まず、Subject 抽象クラスを定義します:
package ObserverPattern;import java.util.ArrayList;
/**
- @author ayqy
- 定义 Subject 抽象类
*/ public abstract class Subject { ArrayList<Observer> observers = new ArrayList<Observer>();//观察者列表
/**
* 注册主题
* @param o 申请注册该主题的 Observer
*/
public void registSubject(Observer o)
{
observers.add(o);
}
/**
* 删除主题
* @param o
*/
public void reomveSubject(Observer o)
{
int index = observers.indexOf(o);
observers.remove(index);
}
/**
* 通知所有观察者
* @param arg 该 Subject 想要传递给 Observers 的数据
*/
public void notifyObservers(Object arg)
{
for(Observer o : observers)
o.update(this, arg);
}
}
注意してください。ここではインターフェースではなく抽象クラスを使用しています。なぜでしょうか?Subject の振る舞いは fixed であり、サブクラスによって拡張する必要がないからです。同様に、Observer 抽象クラスを定義します:
package ObserverPattern;/**
- @author ayqy
- 定义 Observer 抽象类
*/ public abstract class Observer { Subject subject = null;//定义该 Observer 所关注的 Subject
public abstract void update(Subject subject, Object arg);//定义 Observer 的更新接口
}
次に、カスタム Subject である GameServer を実装します:
package ObserverPattern;/**
- @author ayqy
- 定义 GameServer 类,继承自 Subject 基类,负责发布题目
*/ public class GameServer extends Subject{ Question ques;//定义题目
public Question getQues() {
return ques;
}
public void setQues(Question q) {
this.ques = q;
super.notifyObservers(ques);//调用父类方法通知所有 Observer
}
}
P.S. GameServer クラスのメンバー変数 Question は問題情報の簡単なカプセル化であり、Question クラスには問題番号 no と問題内容 content の 2 つの定義と、問題の説明情報を返す toString メソッドが含まれています。次に、カスタム Observer である Player を実装します:
package ObserverPattern;/**
- @author ayqy
- 定义 PlayerA,继承自 Observer 基类,负责接收新题目
*/ public class PlayerA extends Observer{
public PlayerA(Subject sub)
{
subject = sub;
}
@Override
public void update(Subject subject, Object arg) {
Question q = (Question)arg;
System.out.println("PlayerA received " + q.toString());
}
}
P.S. クラス階層をより明確にするために、ここでは Player ベースクラスを定義していません。PlayerB と PlayerC をコピーペーストで簡単に作成できます。詳しくは省略します。
これで、模擬クイズイベントの準備が終了しました。次に、テストクラスを定義してオブザーバーパターンの魅力を示します。
三.効果の例
以下の Test クラスを定義します:
package ObserverPattern;/**
- @author ayqy
- 实现一个测试类,模拟网络游戏答题活动(游戏服务器按时更新题目信息并通知所有参与答题的玩家)
*/ public class Test { public static void main(String[] args) { System.out.println("答题活动即将开始。。"); //创建服务器 GameServer gs = new GameServer(); //创建玩家 ABC Observer playerA = new PlayerA(gs); Observer playerB = new PlayerB(gs); Observer playerC = new PlayerC(gs); //为 AB 注册 Subject,C 对答题不感兴趣,拒绝注册 gs.registSubject(playerA); gs.registSubject(playerB); System.out.println("玩家 AB 成功参与答题活动。。"); System.out.println("答题活动正式开始。。"); gs.setQues(new Question(1, "第一题")); gs.setQues(new Question(2, "第二题")); System.out.println("玩家 A 不想玩了,退出答题活动。。"); gs.reomveSubject(playerA); gs.setQues(new Question(3, "第三题")); System.out.println("玩家 C 想中途加入活动"); gs.registSubject(playerC); gs.setQues(new Question(4, "第四题")); System.out.println("答题活动结束。。"); } }
結果の例:

四.まとめ
上記の例から、オブザーバーパターンの特徴がわかります:
- オブザーバーパターンを利用すると、オブジェクト間の「一対多」の依存関係を簡単に確立できます。
- オブザーバーパターンのメカニズムを利用すると、この依存関係の動的な維持を簡単に実現できます。
コメントはまだありません