PHP观察者模式

观察者模式是PHP设计模式中常用的一种,常用来解耦,一般情况下,会用到3个SPL接口:SplSubjectSplObserverSplObjectStorage,这三个接口的详细介绍可以参考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();
        }
    }
}

上面的代码与注释已经很详细了,其他就不在详述了。