观察者模式
观察者模式:
定义对象的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
设计原则:
在观察者模式中,会改变的是主题(被观察者)的状态以及观察者的数目。用这个模式,你可以改变依赖于主题状态的对象,却不必改变主题。——找出程序中会变化的方面,然后将其和固定不变的方面相分离!
主题和观察者都使用接口:观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者。这样可以让两者之间运作正常,又同时具有松耦合的优点! ——针对接口编程,不针对实现编程!
观察者模式利用“组合”将许多观察者组合进主题中。对象(观察者——主题)之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式产生的。 ——多用组合,少用继承!
使用场景:
订单创建完成后会发送EMAIL和改变订单状态等:http://blog.csdn.net/initphp/article/details/7708006
代码:
<?php /* * 观察者模式 * @date: */ /* * 主题类 */ class Paper{ private $_ob = array(); public function register(Ob $ob){ //注册观察者 $this->_ob[] = $ob; } public function trigger(){ if(!empty($this->_ob)){ foreach($this->_ob as $ob){ $ob->update(); } } } } /* * 观察者接口 */ interface Ob{ public function update(); } class MyOb implements Ob{ public function update(){ echo get_class($this),'<br/>'; } } $obj = new Paper(); $obj->register(new MyOb); $obj->trigger();
观察者模式太常用了,php标准库提供了对应的接口:
观察者接口SplObserver
SplObserver { /* 方法 */ abstract public void update ( SplSubject $subject ) }
主题(被观察者)接口SplSubject
SplSubject { /* 方法 */ abstract public void attach ( SplObserver $observer ) abstract public void detach ( SplObserver $observer ) abstract public void notify ( void ) }
SplSubjectStorage接口
SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess { }
php手册中的例子:
<?php /** * Subject,that who makes news */ class Newspaper implements \SplSubject{ private $name; //private $observers = array(); private $observers; private $content; public function __construct($name) { $this->name = $name; $this->observers = new \SplObjectStorage(); } //add observer public function attach(\SplObserver $observer) { // $this->observers[] = $observer; $this->observers->attach( $observer ); } //remove observer public function detach(\SplObserver $observer) { /* $key = array_search($observer,$this->observers, true); if($key){ unset($this->observers[$key]); } */ $this->observers->detach( $observer ); } //set breakouts news public function breakOutNews($content) { $this->content = $content; $this->notify(); } public function getContent() { return $this->content." ({$this->name})"; } //notify observers(or some of them) public function notify() { foreach ($this->observers as $value) { //SplObjectStorage实现了Iterator $value->update($this); } } } /** * Observer,that who recieves news */ class Reader implements SplObserver{ private $name; public function __construct($name) { $this->name = $name; } public function update(\SplSubject $subject) { echo $this->name.' is reading breakout news <b>'.$subject->getContent().'</b><br>'; } } $newspaper = new Newspaper('Newyork Times'); $allen = new Reader('Allen'); $jim = new Reader('Jim'); $linda = new Reader('Linda'); //add reader $newspaper->attach($allen); $newspaper->attach($jim); $newspaper->attach($linda); //remove reader $newspaper->detach($linda); //set break outs $newspaper->breakOutNews('USA break down!'); //=====output====== //Allen is reading breakout news USA break down! (Newyork Times) //Jim is reading breakout news USA break down! (Newyork Times)
总结:
当新对象要填入的时候,只需要在主题(又叫可观察者)中进行注册(注册方式很多,你也可以在构造的时候,或者框架访问的接口中进行注册),然后实现代码直接在新对象的接口中进行。这降低了主题对象和观察者对象的耦合度。
好的设计模式不会直接进入你的代码中,而是进入你的大脑中。