PHP观察者模式
观察者模式是PHP设计模式中常用的一种,常用来解耦,一般情况下,会用到3个SPL接口:SplSubject
、SplObserver
、SplObjectStorage
,这三个接口的详细介绍可以参考PHP手册,我们这里只是使用,也有很多是自己实现观察者模式,没有用到SPL库的,这里暂且不谈。
先解释一下我理解里的观察者模式是怎么回事:顾名思义,像一个观察者一样,开始观察某个东西A的时候,当A发生某些改变之后,观察者可以做出相应的反应。比如我们常见的微信公众号,我们关注一个微信公众号的之后,每当这个公众号发布新文章的时候,微信服务器就会把这个文章推送给每一个关注的人,这就是微信服务器利用类似观察者模式,观察到公众号发布了新文章,就做出了相应的反应(把文章推给关注者)。
我们来看一下一个实例代码,这个是一个下订单,下单后记录日志、给用户和商城管理者发送邮件的过程,有点长,请耐心仔细看代码逻辑与注释:
/**
* 观察者模式实例 Observer
*/
/**
*订单数据对象简单模拟
*这个是实际需要被观察的对象(Subject),
*但是我们将其独立,
*然后通过构造方法传入到我们模式中的Subject中,
*这样使具体业务更加独立
*/
class Order
{
//订单号
private $id = '';
//用户ID
private $userId = '';
//用户名
private $userName = '';
//价格
private $price = '';
//下单时间
private $orderTime = '';
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
public function __set($name, $value)
{
if (isset($this->$name)) {
$this->$name = $value;
}
}
//用魔术方法获取订单属性,方便扩展
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
}
return "";
}
}
//被观察者, 负责监听类、维护观察者并在变化发生是通知观察者
class OrderSubject implements SplSubject
{
private $observers;
private $order;
public function __construct(Order $order)
{
$this->observers = new SplObjectStorage();
$this->order = $order;
}
//增加一个观察者
public function attach(SplObserver $observer)
{
$this->observers->attach($observer);
}
//移除一个观察者
public function detach(SplObserver $observer)
{
$this->observers->detach($observer);
}
//通知所有观察者
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
//返回主体对象的具体实现,供观察者调用
public function getOrder()
{
return $this->order;
}
}
//观察者(事件类) 记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
class ActionLogObserver implements SplObserver
{
public function update(SplSubject $subject)
{
$order = $subject->getOrder();
//实际应用可能会写到日志文件中,这里直接输出
output("[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]");
}
}
//观察者(事件类) 给用户发送订单确认邮件 (UserMailObserver)
class UserMailObserver implements SplObserver
{
public function update(SplSubject $subject)
{
$order = $subject->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output("Dear {$order->userName}: Your order {$order->id} was confirmed!");
}
}
//观察者(事件类) 给管理人员发订单处理通知邮件 (AdminMailObserver)
class AdminMailObserver implements SplObserver
{
public function update(SplSubject $subject)
{
$order = $subject->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output("Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!");
}
}
function output($string)
{
echo $string . "n";
}
//假设的DB类,便于测试,实际会存入真实数据库
class FakeDB
{
public function save($data)
{
return true;
}
}
Client::test();
//客户端调用 注意
class Client
{
public static function test()
{
//初始化一个订单数据
$order = new Order();
$order->id = 123;
$order->userId = 321;
$order->userName = "God";
$order->price = 0.01;
$order->orderTime = time();
//监听order类
$subject = new OrderSubject($order);
//监听其他类
//$subject2 = new OrderSubject($else);
$actionLogObserver = new ActionLogObserver();
$userMailObserver = new UserMailObserver();
$adminMailObserver = new AdminMailObserver();
//增加一个观察者
$subject->attach($actionLogObserver);
$subject->attach($userMailObserver);
$subject->attach($adminMailObserver);
//向数据库保存订单
$db = new FakeDB();
$result = $db->save($order);
if ($result) {
//保存订单成功通知观察者
$subject->notify();
}
}
}
上面的代码与注释已经很详细了,其他就不在详述了。