面向过程编程(结构化编程),我们强调的是程序的分块结构和流程处理。进入面向对象(OOP)编程领域之后,设计模式成为一项很重要的技术。设计模式处理的是对象的生成及其协作、依赖、耦合等等关系问题。比如,单例模式保证一个类只能被实例化一次,工厂模式可以依据不同的条件生成相应的类的实例(对象)。而观察者模式则一般用来实现“事件”处理,构造软件的事件处理系统。
关于观察者模式(Observer)的实现方法,本人在一篇博客文章中论及过(http://blog.why100000.com/?p=744)。但实际上,PHP5.0 以后,SPL(标准PHP库)提供了方便的对观察者模式的支持,特别是 PHP5.2 以后,SPL 的功能得到了很大的扩充。
观察者模式涉及到两种(两个或多个)类,一个作为被观察对象(subject),另一个作为“观察者”(Observer)。观察者类监视一个或多个观察对象的状态,当其状态变化时,观察者会得到通知。
为了支持观察者模式,SPL 提供了 SplSubject 和 SplObserver 接口。
SplSubject 接口提供了 attach()、detach()、notify() 三个方法。而 SplObserver 接口则提供了 update()方法。
SplSubject 派生类维护了一个状态,当状态发生变化时 - 比如属性变化等,就会调用 notify() 方法,这时,之前在 attach() 方法中注册的所有 SplObserver 实例的 update() 方法就会被调用。
class SubjectDemo implements SplSubject
{
private $observers, $value;
public function __construct()
{
//$observers被声明为数组,表示观察者可以有多个
$this->observers = array();
}
public function attach(SplObserver $observer)
{
$this->observers[] = $observer;
}
public function detach(SplObserver $observer)
{
if($idx = array_search($observer, $this->observers, true))
unset($this->observers[$idx]);
}
public function notify()
{
foreach($this->observers as $observer)
$observer->update($this);
}
public function setValue($value)
{
$this->value = $value;
$this->notify();
}
public function getValue()
{
return $this->value;
}
public function getObserversNumber()
{
return count($this->observers);
}
}
class ObserverDemo1 implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'The new value is '. $subject->getValue() . '<br>';
}
}
class ObserverDemo2 implements SplObserver
{
public function update(SplSubject $subject)
{
echo 'The Number of Observer(s) is(are) '. $subject->getObserversNumber() . '<br>';
}
}
$subject = new SubjectDemo();
$observer1 = new ObserverDemo1();
$observer2 = new ObserverDemo2();
$subject->attach($observer1);
$subject->attach($observer2);
$subject->setValue(100);
从以上代码中看出,观察者可以有多个类。
当 $subject 对象的 setValue() 方法被调用(触发)时,本身被作为参数传递到 $observer1 和 $observer2 对象的 update() 方法中使用。所有观察者的 update() 函数类似事件处理函数。
要扩展事件处理能力,只要从 SplObserver 接口派生一个子类,编写其 update() 函数代码,并附加(attach)到 $subject 对象中即可。
具体到以上代码,有一个缺陷,就是没有实现不同的事件,用其对应的类来处理。这个不是观察者模式本身的问题,需要结合别的模式来解决。
作者:张庆(网眼) 西安 PHP 教育培训中心 2010-7-6
来自“网眼视界”:http://blog.why100000.com
作者微博:http://t.qq.com/zhangking
“十万个为什么”电脑学习网:http://www.why100000.com