SPL(标准PHP库 - Standard PHP Library)是PHP5面向对象功能中重要的部分。原文解释是这样的“The Standard PHP Library (SPL) is a collection of interfaces and classes that are meant to solve common problems”。
SplSubject 和 SplObserver 接口
The SplSubject interface is used alongside SplObserver to implement the Observer Design Pattern.
观察者模式是一种简单的事件系统,包含了两个或更多的互相交互的类。这一模式允许某个类观察另一个类的状态,当被观察类的状态发生变化时,这个模式会得到通知。被观察的类叫subject,负责观察的类叫做Observer 。PHP 提供的 SplSubject 和 SplObserver接口可用来表达这些内容。
SplSubject { /* 方法 */ abstract public void attach ( SplObserver $observer ) abstract public void detach ( SplObserver $observer ) abstract public void notify ( void ) }
SplObserver { /* 方法 */ abstract public void update ( SplSubject $subject ) }
这里,splsubject类维护了一个特定状态,当这个状态发生变化时,他就会调用notify方法,所以之前使用attach注册的splobserver实例的update就会被调用。这里我们实现一个简单地观察者模式的例子
<?php /** * Subject,that who makes news */ class Newspaper implements SplSubject{ private $name; private $observers = array(); private $content; public function __construct($name) { $this->name = $name; } //add observer public function attach(SplObserver $observer) { $this->observers[] = $observer; } //remove observer public function detach(SplObserver $observer) { $key = array_search($observer,$this->observers, true); if($key){ unset($this->observers[$key]); } } //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) { $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)
上例中我们通过数组存储 observer对象,使用数组及可能会出现两个问题:
1、同一个observer可能会被加载多次,导致多次调用同一个对象的update方法。
2、detach中需要通过迭代或者搜索数组来找到要删除的observer对象,导致运行效率降低。
SplObjectStorage类
PHP5提供了SplObjectStorage类,在这里我们可以用来存储observer对象,SplObjectStoraged的实例就像一个数组,但是他所存放的对象是唯一的。SplObjectStorage还可以使用detach直接从集合中删除指定的对象而不用遍历或搜索整个集合。下面看一个SplObjectStorage的例子:
<?php $ObjectStorage = new SplObjectStorage(); class classa { #code... } class classb { #code... } $a = new classa(); $b = new classb(); $ObjectStorage->attach($a); $ObjectStorage->attach($b); $ObjectStorage->attach($a); foreach ($ObjectStorage as $key => $item) { echo ($key+1).'、'.(new ReflectionClass($item))->getName()." "; }
//output
1、classa
2、classb
[Finished in 0.1s]
?>
上例中我们可以看到,在ObjectStorage这个集合中只有1个calssa,尽管我们添加了两次。并且冲集合中删除一个元素也变得极为简单,拿上面的代码来说,只需使用$ObjectStorage->attach($a);即可轻松的将$a从集合中移除。
结合 SplObjectStorage 我们再来修改最上面那个观察者模式的例子
<?php /** * Subject,that who makes news */ class Newspaper implements SplSubject{ private $name; private $observers; private $content; public function __construct($name) { $this->name = $name; $this->observers = new SplObjectStorage(); } //add observer public function attach(SplObserver $observer) { $this->observers -> attach($observer); } //remove observer public function detach(SplObserver $observer) { $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) { $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 '.$subject->getContent()." "; } } $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)