/** *观察者接口 */ interface Observer{ /** *观察者角色需要实现一个update方法,用于处理被观察者发来的通知。 */ public function update(Subject $subject); } /** *具体观察者1 */ class ConcreteObserver1 implements Observer{ private $status; /** *实现接口的update方法 */ public function update(Subject $subject){ $this->status = $subject->getStatus(); } public function getStatus(){ return $this->status; } } /** *具体观察者2 */ class ConcreteObserver2 implements Observer{ private $status; /** *实现接口的update方法 */ public function update(Subject $subject){ $this->status = $subject->getStatus(); } public function getStatus(){ return $this->status; } } /** *被观察者接口 */ interface Subject{ public function addObserver(Observer $observer);//增加订阅 public function removeObserver(Observer $observer);//解除订阅 public function notify();//给所有订阅对象发送通知 } /** *被观察者类 */ class ConcreteSubject implements Subject{ private $observers = array(); private $status; public function addObserver(Observer $observer){ if(!in_array($observer, $this->observers, true)){ $this->observers[] = $observer; } } public function removeObserver(Observer $observer){ if(false!==($index = array_search($observer, $this->observers, true))){ unset($this->observers[$index]); } } public function setStatus($status){ $this->status = $status; } public function getStatus(){ return $this->status; } public function notify(){ foreach($this->observers as $observer){ $observer->update($this); } } } $subject = new ConcreteSubject(); $subject->setStatus('status is ok!'); $observer1 = new ConcreteObserver1(); $subject->addObserver($observer1); $observer2 = new ConcreteObserver2(); $subject->addObserver($observer2); $subject->notify(); echo 'Observer1:'.$observer1->getStatus().'<br>'; echo 'Observer2:'.$observer2->getStatus().'<br>';观察者模式的应用场景:
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
观察者模式的优点:
1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。
2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
观察者模式的缺陷:
1、 松偶合导致代码关系不明显,有时可能难以理解。
2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。(毕竟只是简单的遍历)