观察者模式
问题引入
一个系统在用户登录的时候,经常要记录一些东西:session、登录次数、统计在线时长等;如果这么多的操作按照面向过程的方法编写,使一个对象变得复杂,它要操作这么多的事情,这样也违反单一功能原则。
如果这些操作以插件形式加载或移除,那么登录只完成它的单一功能操作。
这样就可以使用观察者模式,后期再增加相关功能模块,不需要太多工作就可以加载同步;也不用修改登录的功能(符合开闭原则);
缺点就是因为各个插件模块分散,后面功能可能重叠之前的功能,导致数据错误,所以在使用时要尽量注释或者整理好文档
使用场景
当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的
如何使用
php已经提供了主题接口类和观察者接口类,还有一个SplObjectStorage数据结构对象容器——实现了Countable,Iterator,Serializable,ArrayAccess四个接口。可实现统计、迭代、序列化、数组式访问等功能。
主题类(被观察者)
namespace Observer;use SplObjectStorage;use SplObserver;use SplSubject;class User implements SplSubject{ /** * @var SplObjectStorage */ private $observers; public function __construct() { $this->observers = new SplObjectStorage(); } /** * 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) { $this->observers->attach($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) { $this->observers->detach($observer); } /** * Notify an observer * @link http://php.net/manual/en/splsubject.notify.php * @return void * @since 5.1.0 */ public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } }
观察者1
namespace Observer;use SplObserver;use SplSubject;class UserObserver implements 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 string * @since 5.1.0 */ public function update(SplSubject $subject) { echo '我是观察者1:获取用户信息' . '<br>'; } }
观察者2
namespace Observer;use SplSubject;class OntimeObserver implements \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 string * @since 5.1.0 */ public function update(SplSubject $subject) { echo '我是观察者2:记录用户在线时间' .'<br>'; } }
实施调用
use Observer\OntimeObserver;use Observer\User;use Observer\UserObserver; spl_autoload_register(function($class_name) { $class_file = realpath(dirname(__FILE__)."/../") .'/' . str_replace('\\','/', $class_name) . '.php'; if (file_exists($class_file)) { include $class_file; } else { echo "加载文件失败"; } });//实例化主题类$user = new User();//添加观察者$user->attach(new UserObserver()); $user->attach(new OntimeObserver());//通知观察者$user->notify();
作者:PHP的艺术编程
链接:https://www.jianshu.com/p/09cddd2aaad4