概念
观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
当一个对象状态发生改变后,会影响到其他几个对象的改变,这时候可以用观察者模式。
观察者模式符合接口隔离原则,实现了对象之间的松散耦合。
别名
发布-订阅<Subscribe>模式
模型-视图<View>模式
源-收听者<Listener>模式
从属者模式
角色
抽象主题(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
优点和缺点
优点
观察者和主题之间的耦合度较小;
支持广播通信;
缺点
由于观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。这可能会引起意外的更新。
适用场景
当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。