@[TOC](观察者模式)
观察者模式定义
观察者模式又称发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象,当主题对象发生变化时会通知所有观察者对象,使他们能够更新自己
从故事开始
老高:今天摸鱼看股票又被老板抓住了,半天工资没了
老摸:你可以贿赂一下前台小美,等老板回公司的时候就给你发消息,你就可以及时切换到工作面板了
老高:好主意,我这就去买奶茶
用代码描述故事
前台秘书类 (监听:用来监听老板有没有回来)
class Secretary
{
public $observers = [];
public $action = '';
// 添加
public function attach(StockObserver $observer)
{
$this->observers = array_merge($this->observers, $observer);
return $this;
}
// 通知
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update();
}
}
public function setAction($name)
{
return $this->action = $name;
}
}
看股票的同事 (观察者:老板来了改变自己的状态)
class StockObserver
{
public $name = '';
public $sub;
public function __construct($name,Secretary9 $sub)
{
$sub->setAction($name);
}
public function update()
{
$action = $this->sub->action;
return $this->name + "关闭股票继续工作!"+$action;
}
}
控制器中使用观察者模式 (小美如何向同事发消息)
class Controller
{
public function run()
{
$tongzhi = new Secretary();
$laogao = new StockObserver("老高", $tongzhi);
$laomo = new StockObserver("老摸", $tongzhi);
$tongzhi->attach($laogao);
$tongzhi->attach($laomo);
$tongzhi->action = "老板来了";
return $tongzhi->notify();
}
}
解耦
故事讲完了,但是代码也实现了,但是问题也出现了,
问题:
- 如果有的同事在看直播而不是股票呢,那小美岂不是通知错
- 如果小美休假小帅值班,那通知者就是小帅而不是小美
对于这种情况最简单粗暴的方式就是加一个前台小帅秘书类 和一个看直播的同事类
但是这样代码重复就很多了,所以这段代码需要解耦
抽离前台秘书类
前台秘书类接口
interface Subject
{
public function attach(Observer $observer);
public function notify();
public function setAction($name);
}
前台秘书小美类
class XiaoMei implements Subject
{
public $observers = [];
public $action = '';
// 添加
public function attach(Observer $observer)
{
$this->observers = array_merge($this->observers, $observer);
return $this;
}
// 通知
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update();
}
}
public function setAction($name)
{
return $this->action = $name;
}
}
前台秘书小帅类
与小美的代码类似此处略
抽离观察者
摸鱼同事接口类
abstract class Observer
{
public $name = '';
public $sub;
public function __construct($name,Subject $sub)
{
$sub->setAction($name);
}
abstract public function update();
}
看股票同事接口类
class StockObserver extends Observer
{
public function update()
{
$action = $this->sub->action;
return $this->name + "关闭股票继续工作!"+$action;
}
}
看直播同事接口类
class LiveObserver extends Observer
{
public function update()
{
$action = $this->sub->action;
return $this->name + "关闭直播继续工作!"+$action;
}
}
控制器中使用观察者模式
class Controller
{
public function run()
{
$tongzhi = new XiaoMei();
$laogao = new StockObserver("老高", $tongzhi);
$laomo = new NBAObserver("老摸", $tongzhi);
$tongzhi->attach($laogao);
$tongzhi->attach($laomo);
$tongzhi->action = "老板来了~~";
return $tongzhi->notify();
}
}
总结
观察者模式是将一个系统分割成一系列相互写作的类,有一个很不好的副作用就是需要维护相关对象的一致性。一个主题subject(秘书)可以有任意个观察者observer(同事),一但主题subject(秘书)发生改变,所有的观察者observer(同事)都会得到通知
什么时候使用呢?
当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象待改变的情况下,这个时候应该考虑使用观察者模式
有什么优势呢
观察者模式所做的工作其实就是在解耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化
观察者模式的不足
- 抽象通知者Subject(前台秘书类接口)还是依赖抽象观察者Observer(摸鱼同事接口类),万一没有抽象观察者这样的接口,我这个功能就完不成了,
- 另外每一个功能并不一定是更新(update)的时候才会使用,或者隐藏(hidden)的时候也会使用,更新和隐藏者两个是不同名的方法