Observerパターン[Java]
- 作者:結城 浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
今回は「Observer」パターンについて学びます.
- Observer #とは
- Observerインタフェース(抽象Observer役)
- NumberGeneratorクラス(抽象Subject役)
- RandomNumberGeneratorクラス(具象Subject役)
- DigitObserverクラス(具象Observer役1)
- GraphObserverクラス(具象Observer役2)
- Mainクラス
- 結果例
- 「観測」というより「通知させて貰っている」
Observer #とは
書籍には「観察者」と訳されていますが,ボクはシュタゲが好きなので「観測者」と呼びます.
Observer Patternでは,観測対象,つまり被験者(Subject)の状態が変化すると,
観測者(Observer)に対して通知されます.状態変化に応じた処理を記述するときに有効です.
Subjectは状態を持っている役で,Observerは状態変化を通知してもらう役です.
Subject-Observerの関係はTwitterで例えると,
アイドル(Subject)の日常生活の呟きをフォロワー(Observer)であるボクらが見ている感じ.
Sample ProgramのClass Diagramは以下.
ここでは
抽象Subject役:NumberGeneratorクラス
具象Subject役:RandomNumberGeneratorクラス
抽象Observer役:Observerインタフェース
具象Observer役:DigitObserverクラス,GraphObserverクラス
になります.
Observerインタフェース(抽象Observer役)
public interface Observer { /** 状態の更新を伝達 抽象クラス */ public abstract void update(NumberGenerator generator); }
update()で状態が更新されたことをSubject役(NumberGenerator)に教えてもらいます.
NumberGeneratorクラス(抽象Subject役)
import java.util.ArrayList; import java.util.ListIterator; public abstract class NumberGenerator { /** Observerの保持 */ private ArrayList<Observer> observers = new ArrayList<Observer>(); /** * Observerの追加 * @param observer */ public void addObserver(Observer observer) { observers.add(observer); } /** * Observerの削除 * @param observer */ public void deleteObserver(Observer observer) { observers.remove(observer); } /** * Observerへ通知 */ public void notifyObservers() { ListIterator<Observer> it = observers.listIterator(); while(it.hasNext()) { Observer o = it.next(); o.update(this); } } /** 数の取得 抽象クラス */ public abstract int getNumber(); /** 数の生成 抽象クラス */ public abstract void execute(); }
Observer役を登録・削除するメソッドを持ちます.
notifyObservers()では,Observer全員に自分の状態が更新されたことを伝えます.
RandomNumberGeneratorクラス(具象Subject役)
import java.util.Random; public class RandomNumberGenerator extends NumberGenerator { private Random random = new Random(); private int number; @Override public int getNumber() { return number; } @Override public void execute() { for (int i = 0; i < 20; i++) { number = random.nextInt(30); notifyObservers(); } } }
execute()で乱数を20個生成し,その都度notifyObservers()で観測者に通知します.
DigitObserverクラス(具象Observer役1)
public class DigitObserver implements Observer { @Override public void update(NumberGenerator generator) { System.out.println("DigitObserver:" + generator.getNumber()); }
観測した数を「数字」で表示します.
GraphObserverクラス(具象Observer役2)
public class GraphObserver implements Observer { @Override public void update(NumberGenerator generator) { System.out.print("GraphObserver:"); int count = generator.getNumber(); for (int i = 0; i < count; i++) { System.out.print("*"); } System.out.println(""); } }
観測した数の分,アスタリスクを表示します.
Mainクラス
public class Main { public static void main(String[] args) { /** 被験者の生成 */ NumberGenerator generator = new RandomNumberGenerator(); /** 観測者の追加 */ generator.addObserver(new DigitObserver()); generator.addObserver(new GraphObserver()); /** 被験者の行動 */ generator.execute(); } }
結果例
DigitObserver:12 GraphObserver:************ DigitObserver:6 GraphObserver:****** DigitObserver:14 GraphObserver:************** DigitObserver:19 GraphObserver:******************* DigitObserver:27 GraphObserver:*************************** DigitObserver:14 GraphObserver:************** DigitObserver:15 GraphObserver:*************** DigitObserver:0 GraphObserver: DigitObserver:27 GraphObserver:*************************** DigitObserver:4 GraphObserver:**** DigitObserver:27 GraphObserver:*************************** DigitObserver:21 GraphObserver:********************* DigitObserver:2 GraphObserver:** DigitObserver:6 GraphObserver:****** DigitObserver:17 GraphObserver:***************** DigitObserver:5 GraphObserver:***** DigitObserver:10 GraphObserver:********** DigitObserver:6 GraphObserver:****** DigitObserver:1 GraphObserver:* DigitObserver:15 GraphObserver:***************
「観測」というより「通知させて貰っている」
Observer役は能動的に「観測」しているというよりかは,
Subject役から受動的に「通知」されているのを待っていることから,
Publish(発行)-Subscribe(購読) (Pub/Sub) Patternと呼ばれることがあるみたいです.
Subject役は「観測」できることから,Observableです.
ふむ.Reactive Extensionsで見かけたことのある用語ですね.いずれまたまとめたいです.
また,MVC(Model View Controller)PatternのModel-Viewの関係に対応しています.
一般に1つのModelに複数のViewが対応しています.
ゲームキューブのコントローラーを握り,コントローラーを通して,
マリオのモデルを操作して,そのモデルの動作をテレビのビューを通じて見るって感じだとわかりやすい?