<转>PHP设计模式之观察者模式(Observer)

观察者模式: 又称为发布订阅模式,指当一个主体对象(Subject)发生改变时,依赖它的多个观察对象(Observer)都得到通知(notify)并自动更新(update)。

PHP从5.1.0开始提供了SplSubject接口和SplObserver接口,为我们具体应用观察者模式提供了便利。著名的的PHP框架如ZendFramework、Symfony均深度使用了观察者模式。那么究竟在什么场景下需要用到观察者模式呢,以下举例说明:

假如我们应用的订单模块中(Subject=Order<->OrderSubject),用户成功下单之后,我们需要做以下3件事情(Observers):

  • 记录业务数据日志 (ActionLogObserver)
  • 给用户发送订单确认邮件 (UserMailObserver)
  • 给管理人员发订单处理通知邮件 (AdminMailObserver)
<?php
//订单数据对象简单模拟,这个是实际需要被观察的对象(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();
         //实际应用可能会写到日志文件中,这里直接输出
         echo "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]";
    }
}

//给用户发送订单确认邮件 (UserMailObserver)
class UserMailObserver implements SplObserver{
    public function update(SplSubject $subject) {
         $order = $subject->getOrder();
         //实际应用会调用邮件发送服务如sendmail,这里直接输出
         echo "Dear {$order->userName}: Your order {$order->id} was confirmed!";
    }
}

//给管理人员发订单处理通知邮件 (AdminMailObserver)
class AdminMailObserver implements SplObserver{
    public function update(SplSubject $subject) {
         $order = $subject->getOrder();
         //实际应用会调用邮件发送服务如sendmail,这里直接输出
         echo "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!";
    }
}

//假设的DB类,便于测试,实际会存入真实数据库
class FakeDB{
    public function save($data){
        return true;
    }
}

//初始化一个订单数据
$order = new Order();
$order->id = 1001;
$order->useId = 9527;
$order->userName = "God";
$order->price = 20.0;
$order->orderTime = time();
//绑定观察者
$subject = new OrderSubject($order);
$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();
}

Symfony框架之事件监听(EventDispatcher)观察者模式应用简要分析

//相当于被观察者SplSubject
interface EventDispatcherInterface
{
    //相当于上面的OrderSubject->notify方法
    public function dispatch($eventName, Event $event = null);

    //相当于上面的OrderSubject->attach方法
    public function addListener($eventName, $listener, $priority = 0);

    //添加订阅者,一个订阅者可以监听多个事件
    public function addSubscriber(EventSubscriberInterface $subscriber);

    //相当于上面的OrderSubject->detach方法
    public function removeListener($eventName, $listener);

    //移除订阅者
    public function removeSubscriber(EventSubscriberInterface $subscriber);

    //获得观察者(监听者),主要在dispatch的时候用到
    public function getListeners($eventName = null);

    //判断是否已经注册了该观察者(监听者)
    public function hasListeners($eventName = null);
}

//被观察者的具体实现
class EventDispatcher implements EventDispatcherInterface
{
    //具体实现代码省略,大家可以自己去看Symfony代码

    //监听者排序,实现了监听处理优先级功能
    private function sortListeners($eventName)
    {
        $this->sorted[$eventName] = array();

        if (isset($this->listeners[$eventName])) {
            krsort($this->listeners[$eventName]);
            $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
        }
    }
}
//Event,实际上的被观察者超类,所以观察者(监听者)都是注册在相应的Event上面的
class Event
{
    //是否已经停止事件继续派发
    private $propagationStopped = false;

    //让Event能获得当前的派发器
    private $dispatcher;

    //让Event能够获得自己的名称
    private $name;

    public function isPropagationStopped()
    {
        return $this->propagationStopped;
    }

    //在监听处理方法中调用,阻止剩余监听器的调用
    public function stopPropagation()
    {
        $this->propagationStopped = true;
    }

    //让Event能获得当前的派发器,这个方法在EventDispatcher::dispatch方法中调用
    public function setDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    //返回当前Event的派发器
    public function getDispatcher()
    {
        return $this->dispatcher;
    }

    public function getName()
    {
        return $this->name;
    }

    //让Event能够获得自己的名称,这个方法在EventDispatcher::dispatch方法中调用
    public function setName($name)
    {
        $this->name = $name;
    }
}
//一个Event的具体实现,是实际的被观察者,这个Event与前端开发中的Event类似而又有所不同,
//这个Event不需要有用户操作,完全是我们在代码中指定的(如某个对象发生变化的时候抛出一个Event)

namespace Acme\StoreBundle\Event;

use Symfony\Component\EventDispatcher\Event;
use Acme\StoreBundle\Order;

class FilterOrderEvent extends Event
{
    protected $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function getOrder()
    {
        return $this->order;
    }
}
//观察者(监听者)的具体实现,观察者非常自由,SplObserver中约定的update方法命名在Symfony中也
//是完全自定义的,因为在注册观察者的时候会指定相关的具体update方法
use Symfony\Component\EventDispatcher\Event;

class ActionLogListener
{
    // ...

    public function onSaveAction(Event $event)
    {
        // ... do something
        $order = $event->getOrder();
        //实际应用可能会写到日志文件中,这里直接输出
        echo "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]";
    }
}

class UserMailListener
{
    // ...

    public function onSaveAction(Event $event)
    {
        // ... do something
        $order = $event->getOrder();
        //实际应用会调用邮件发送服务如sendmail,这里直接输出
        echo "Dear {$order->userName}: Your order {$order->id} was confirmed!";
    }
}
// 具体调用

use Acme\StoreBundle\StoreEvents;
use Acme\StoreBundle\Order;
use Acme\StoreBundle\Event\FilterOrderEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;

//初始化一个被观察者(这里更像一个管理器)
$dispatcher = new EventDispatcher();

//增加一个store.order事件的监听(观察者)
$actionLoglistener = new ActionLogListener();
$userMailListener = new UserMailListener();
$dispatcher->addListener("store.order", array($actionLoglistener, 'onSaveAction'));
$dispatcher->addListener("store.order", array($userMailListener, 'onSaveAction'));

// 任何方式获得Order对象
$order = new Order();
// order数据的填充处理

// 初始化FilterOrderEvent(具体被观察的对象)
$event = new FilterOrderEvent($order);

// 事件派发,相当于调用了Subject->notify();
// ActionLogListener::onSaveAction() 和 UserMailListener::onSaveAction()均会被调用
$dispatcher->dispatch("store.order", $event);

Symfony Event Dispatcher 特点总结

  • 通过增加一个Event的全局命名,使得可以使用一个事件派发器管理多种类型的事件派发(也就是实现了对多个不同的被观察者统一的监听控制)
  • 实现了观察者(监听者)的优先级排序功能,这样最终的具体处理变的更加强大
  • 增加了订阅者角色,实现了一个对象对多个事件监听的同时注册和统一处理

posted on 2015-10-18 21:01  hahahahahai12  阅读(265)  评论(0)    收藏  举报

导航