观察者模式(Observer Design Pattern) 也被称为 发布订阅模式(Publish-SubscribeDesign Pattern),在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者 (Observer)。
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Feeds,本质上都是观察者模式。不同的应用场景和需求下,这个模式也有截然不同的实现方式,有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。
PHP中提供了 观察者(observer) 和 被观察者(subject) 的接口 SplSubject (PHP >= 5.1.0),https://www.php.net/manual/en/class.splsubject.php
SplSubject {
abstract public void attach ( SplObserver $observer )
abstract public void detach ( SplObserver $observer )
abstract public void notify ( void )
}
这里用PHP实现一个简单的观察者模式,设定场景为登录操作,如果登录尝试次数小于3次认为登录成功,否则登录失败。同时可以设定登录之后给指定的接口URL推送数据。代码如下:
<?php
/**
* 根据 登录次数 和 推送URL 进行观察,
* 定义user类,实现SplSubject接口
*/
class User implements SplSubject {
public $login_count; //登录次数
public $push_url; //推送的URL
protected $observers; //储存被观察的对象
public function __construct($login_count, $push_url) {
$this->login_count = $login_count;
$this->push_url = $push_url;
$this->observers = new SplObjectStorage();
}
/**
* 登录操作
*/
public function login() {
//todo 登录的具体逻辑实现...
//登录后,触发通知
$this->notify();
}
/**
* 观察
*/
public function attach(SPLObserver $observer) {
$this->observers->attach($observer);
}
/**
* 取消观察
*/
public function detach(SPLObserver $observer) {
$this->observers->detach($observer);
}
/**
* 通知
*/
public function notify() {
$this->observers->rewind();
//循环获取储存的对象
while ($this->observers->valid()) {
$observer = $this->observers->current();
$observer->update($this);
$this->observers->next();
}
}
}
/**
* 假设现在有个 "安全模块" 要来观察, 设定登录次数小于3属于正常操作,否则属于异常操作
*/
class Security implements SPLObserver {
public function update(SplSubject $subject) {
if ($subject->login_count < 3) {
echo '当前第' . $subject->login_count . '次登录,正常操作.';
} else {
echo '当前第' . $subject->login_count . '次登录,出现异常!';
}
echo PHP_EOL;
}
}
/**
* 假设现在又有个 "推送模块" 要来观察,登录成功后给xxx接口推送一条记录
*/
class Push implements SPLObserver {
public function update(SplSubject $subject) {
echo "给 {$subject->push_url} 推送登录数据";
echo PHP_EOL;
}
}
//实施观察, 实例化User
$user = new User(1, 'www.xxx.com');
//安全模块 的观察
$user->attach(new Security());
//推送模块 的观察
$user->attach(new Push());
//执行登录操作
$user->login();
/*
当前第1次登录,正常操作.
给 www.xxx.com 推送登录数据
*/
上面实例化User类的时候传递了“登录次数”值为1,因此返回 “当前第1次登录,正常操作.”,如果改成3,则会返回 “当前第3次登录,出现异常!”。
按照上面代码的设定,我们可以灵活的增加更多的观察者,比如登录成功后给用户增加积分、更新登录时间 等等。
源代码:https://gitee.com/rxbook/php_design_pattern/blob/master/code06_Observer.php