概念
观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
当一个对象状态发生改变后,会影响到其他几个对象的改变,这时候可以用观察者模式。
观察者模式符合接口隔离原则,实现了对象之间的松散耦合。
别名
发布-订阅
模型-视图
源-收听者
从属者模式
角色
抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
UML图
代码
示例代码
在PHP SPL中已经提供SplSubject和SqlOberver接口,源码如下:
/** * The <b>SplSubject</b> interface is used alongside * <b>SplObserver</b> to implement the Observer Design Pattern. * @link http://php.net/manual/en/class.splsubject.php */ interface SplSubject { /** * Attach an SplObserver * @link http://php.net/manual/en/splsubject.attach.php * @param SplObserver $observer <p> * The <b>SplObserver</b> to attach. * </p> * @return void * @since 5.1.0 */ public function attach (SplObserver $observer); /** * Detach an observer * @link http://php.net/manual/en/splsubject.detach.php * @param SplObserver $observer <p> * The <b>SplObserver</b> to detach. * </p> * @return void * @since 5.1.0 */ public function detach (SplObserver $observer); /** * Notify an observer * @link http://php.net/manual/en/splsubject.notify.php * @return void * @since 5.1.0 */ public function notify (); } /** * The <b>SplObserver</b> interface is used alongside * <b>SplSubject</b> to implement the Observer Design Pattern. * @link http://php.net/manual/en/class.splobserver.php */ interface SplObserver { /** * Receive update from subject * @link http://php.net/manual/en/splobserver.update.php * @param SplSubject $subject <p> * The <b>SplSubject</b> notifying the observer of an update. * </p> * @return void * @since 5.1.0 */ public function update (SplSubject $subject); }
下面我们根据这两个spl接口,写自己的代码:
<?php header('Content-type:text/html;charset=utf-8'); /** * Class Subject 主题 */ class Subject implements SplSubject { private $_observers = []; /** * 实现添加观察者方法 * * @param SplObserver $observer */ public function attach(SplObserver $observer) { if (!in_array($observer, $this->_observers)) { $this->_observers[] = $observer; } } /** * 实现移除观察者方法 * * @param SplObserver $observer */ public function detach(SplObserver $observer) { if (false !== ($index = array_search($observer, $this->_observers))) { unset($this->_observers[$index]); } } /** * 实现提示信息方法 */ public function notify() { foreach ($this->_observers as $observer) { $observer->update($this); } } /** * 设置数量 * * @param $count */ public function setCount($count) { echo "数据量加" . $count . '<br>'; } /** * 设置积分 * * @param $integral */ public function setIntegral($integral) { echo "积分量加" . $integral . '<br>'; } } /** * Class Observer1 观察者一 */ class Observer1 implements SplObserver { public function update(SplSubject $subject) { $subject->setCount(10); } } /** * Class Observer2 观察者二 */ class Observer2 implements SplObserver { public function update(SplSubject $subject) { $subject->setIntegral(10); } } /** * Class Client 客户端 */ class Client { /** * 测试方法 */ public static function test() { // 初始化主题 $subject = new Subject(); // 初始化观察者一 $observer1 = new Observer1(); // 初始化观察者二 $observer2 = new Observer2(); // 添加观察者一 $subject->attach($observer1); // 添加观察者二 $subject->attach($observer2); // 消息提示 $subject->notify();//输出:数据量加1 积分量加10 // 移除观察者一 $subject->detach($observer1); // 消息提示 $subject->notify();//输出:数据量加1 积分量加10 积分量加10 } } // 执行测试 Client::test();
运行结果
数据量加10 积分量加10 积分量加10
优点和缺点
优点
观察者和主题之间的耦合度较小;
支持广播通信;
缺点
由于观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。这可能会引起意外的更新。
适用场景
當一個抽像模型有兩個方面,其中一個方面依賴另一個方面。
當對一個對象的改變需要同時改變其它對象,而不知道具體有多少個對象待改變。
當一個對象必須通知其它對象,而它又不能假定其它對像是誰。換句話說,你不希望這些物件是緊密耦合的。