Observer パターンとは
プログラム内のオブジェクトの状態を観察(英: observe)するようなプログラムで使われるデザインパターンの一種。出版-購読型モデルとも呼ばれる。
観察対象の状態をが変化すると、観察者に対して通知される。Observerパターンは状態変化に応じた処理を記述するときに有効である。
example
数を生成するオブジェクトを観察者が観察して、その値を表示するというプログラムを作成する。 ただし、表示方法は観察者によって異なる。DigitObserverは値で数字を表示し、GraphObserverは値を簡易グラフで表示する。
Observer インターフェース
観察者を表現するインタフェース。
<?php interface Observer { public function update(NumberGenerator $generator); }
NumberGenerator クラス
数を生成する抽象クラス。
<?php abstract class NumberGenerator { private $observers = []; public function addObserver(Observer $observer) { $this->observers[] = $observer; } public function displayObservers() { echo "Observers:" . PHP_EOL; foreach($this->observers as $k => $v) { echo $k+1 . ":" . get_class($v) . PHP_EOL; } echo PHP_EOL; } public function deleteObserver(Observer $observer) { foreach ($this->observers as $k => $v) { if ($v === $observer) { unset($this->observers[$k]); return; } } } public function notifyObservers() { foreach ($this->observers as $observer) { $observer->update($this); } } abstract public function getNumber(): int; abstract public function execute(); }
RandomNumberGenerator クラス
NumberGeneratorのサブクラスで、乱数を生成する。
<?php class RandomNumberGenerator extends NumberGenerator { private $number; public function getNumber(): int { return $this->number; } public function execute() { $this->displayObservers(); for ($i = 0; $i < 10; $i++) { $this->number = rand(0, 30); $this->notifyObservers(); } } }
DigitObserver クラス
Observerインターフェースを実装しているクラスで、観察した数を「数字」で表示する。sleep(1) は表示の様子をわかりやすくするため。
<?php class DigitObserver implements Observer { public function update(NumberGenerator $generator) { echo "DigitObserver:" . $generator->getNumber() . PHP_EOL; sleep(1); } }
GraphObserver クラス
Observerインターフェースを実装するクラス。
<?php class GraphObserver implements Observer { public function update(NumberGenerator $generator) { echo "GraphObserver:"; for ($i = 0; $i < $generator->getNumber(); $i++) { echo "*"; } echo PHP_EOL; sleep(1); } }
Main
テスト動作用
<?php $generator = new RandomNumberGenerator(); $observer1 = new DigitObserver(); $observer2 = new GraphObserver(); $generator->addObserver($observer1); $generator->addObserver($observer2); $generator->execute(); $generator->deleteObserver($observer2); $generator->execute();
実行結果
$ php Main.php Observers: 1:DigitObserver 2:GraphObserver DigitObserver:5 GraphObserver:***** DigitObserver:23 GraphObserver:*********************** DigitObserver:7 GraphObserver:******* DigitObserver:19 GraphObserver:******************* DigitObserver:14 GraphObserver:************** DigitObserver:19 GraphObserver:******************* DigitObserver:22 GraphObserver:********************** DigitObserver:18 GraphObserver:****************** DigitObserver:9 GraphObserver:********* DigitObserver:3 GraphObserver:*** Observers: 1:DigitObserver DigitObserver:20 DigitObserver:12 DigitObserver:3 DigitObserver:20 DigitObserver:9 DigitObserver:8 DigitObserver:12 DigitObserver:17 DigitObserver:19 DigitObserver:13
ソースコード
役割
Subject役
Subject役は観察される側を表す。観察者であるObserver役を登録するメソッドと、削除するメソッドを持っている。 サンプルコードにおけるNumberGeneratorクラス。
ConcreteSubject役
具体的な観察される側を表す。状態が変化したら、そのことを登録されているObserver役に伝える。 サンプルコードにおけるRandomNumberGeneratorクラス。
Observer役
Subject役から、「状態が変化した」と通知してもらう役。そのためのメソッドがupdateメソッド。 サンプルコードにおけるObserverインターフェース。
ConcreteObserver役
具体的なObserver。updateメソッドが呼び出されると、そのメソッドの中でSubject役の現在の状態を取得する。 サンプルコードにおけるDigitObserverクラス、GraphObserverクラス。
まとめ
「観察」というより「通知」。Observer役はSubject役から「通知」されるのを受動的に待っていることになる。そのためObserverパターンはPublish-Subscribeパターンと呼ばれることもある。
関連パターン
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (399件) を見る