说明
这个设计模式很常用、重要, 所以要好好看。这是一个非常经典的设计模式, 解决了不少问题, 在实际项目中应用比较广泛。
先简单说一下设计模式的定义: 一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。观察者模式实现了低耦合,非侵入式的通知与更新机制。
举个🌰, 大家都知道, 今年的猪肉价格让人有望而却步, 让原本就不富裕的家庭更是雪上加霜, 我有个想法就是, 让这个超市猪肉价格一降价就通知我, 好让我能及时以较低价格购买。 我这个时候我就需要观察者, 来观察猪肉的价格, 然后及时通知我。
示例
<?php /** * Created by 憧憬. */ /** * 观察者约束 * Interface Observer */ interface Observer { // 到时候接受到通知及时作出反应 public function update(); } /** * 被观察者 到时候也好通知观察者 * Interface Observable */ interface Observable { // 添加观察者 public function addObserver(Observer $observer); // 删除观察者 public function deleteObserver(Observer $observer); // 通知观察者 public function notifyObserver(); } // ///** // * 猪肉 // * Class Pork // */ //class Pork implements Observable { // // public $observableList; // // public $price; // // public function addObserver(Observer $observer) // { // $this->observableList[] = $observer; // } // // public function deleteObserver(Observer $observer) // { // foreach ($this->observableList as $k => $v) { // if ($v === $observer) { // unset($this->observableList[$k]); // } // } // } // // public function notifyObserver() // { // foreach ($this->observableList as $observer) { // $observer->update(); // } // } // // /** // * 设置猪肉价格 // * @param $price // * @author: 憧憬 // */ // public function setPrice($price) // { // $this->price = $price; // $this->notifyObserver(); // } // //} // /** * 博主自己 * Class Aoppp */ class Aoppp implements Observer { public function update() { echo '憧憬 有你的通知来了 快快行动'; } } /** * 李四也想吃猪肉 * Class Lisi */ class Lisi implements Observer { public function update() { echo '李四 有你的通知来了 快快行动'; } } //$pork = new Pork(); // //$pork->addObserver(new Aoppp()); //$pork->addObserver(new Lisi()); //$pork->setPrice(100); /** * 这个时候你发现 可以随便通知任何人, 也可以移除观察者 但是每个想观察的对象都要写这个函数 有点重复 * 我们可以抽离出来 */ class ObserverUtil implements Observable { public $observableList; public function addObserver(Observer $observer) { $this->observableList[] = $observer; } public function deleteObserver(Observer $observer) { foreach ($this->observableList as $k => $v) { if ($v === $observer) { unset($this->observableList[$k]); } } } public function notifyObserver() { foreach ($this->observableList as $observer) { $observer->update(); } } } /** * 将猪肉类改为继承 * Class Pork */ class Pork extends ObserverUtil { public $price; /** * 设置猪肉价格 * @param $price * @author: 憧憬 */ public function setPrice($price) { $this->price = $price; $this->notifyObserver(); } } $pork = new Pork(); $pork->addObserver(new Aoppp()); $pork->addObserver(new Lisi()); $pork->setPrice(100); // 这样的话 你要监听鸡肉 还是什么乱七八糟的 都要方便很多了
观察者模式在实际项目的应用中非常常见,比如你到 ATM
机器上取钱,多次输错密码,卡就会被 ATM
吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生; 第三,初始化 ATM
机屏幕,返回最初状态,你不能因为就吞了一张卡,整个 ATM
都不能用了吧,一般前两 个动作都是通过观察者模式来完成的。
这个设计模式之后变种了一个发布/订阅
的模式, 这个设计模式也是比较容易理解, 大家可以去了解下。
那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是 观察者模式非常重要的功能。使用观察者模式也有两个重点问题要解决:
- 广播链的问题
如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了 一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器...,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双 重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消 息最多转发一次(传递两次),这还是比较好控制的;
- 异步处理问题
被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个 大家有时间看看 Message Queue,就会有更深的了解。