观察者模式

1、先看PHP观察者模式的实现:

  • 想要使用事件、必须实现事件的基类、统一的addObserver和trigger方法
  • 定义统一接口、所有的观察者都要实现此接口
//事件的基类
abstract class BaseEvent
{
    private static $observer;
    //添加观察者
    public function addObserver($obj)
    {
        self::$observer[] = $obj; 
    }
    //触发事件、通知所有的观察者
    public function trigger()
    {
        foreach(self::$observer as $observer){
            $observer->update();
        }  
    }
}
//作为观察者要实现的接口
interface ObserverInterface
{
    public function update();
}
//具体的一个事件类、要继承事件基类
class Event extends BaseEvent
{
    public function test()
    {
        //执行事件
        echo 'test execute success. notify observer <br />';
        $this->trigger();
    }
}
//观察者实现接口
class Observer1 implements ObserverInterface
{
    public function update()
    {
    
        echo 'observer 1 update<br />';
    }
}
class Observer2 implements ObserverInterface
{
    public function update()
    {
        echo 'observer 2 update<br />';
    }
}


$e = new Event();
//添加两个观察者
$o1 = new Observer1();
$o2 = new Observer2();

$e->addObserver($o1);
$e->addObserver($o2);

$e->test();
//输出
//test execute success. notify observer 
//observer 1 update
//observer 2 update

这种实现方式的好处是:

  • 直接addObserver就好、事件完成之后直接触发就行了、因为观察者实现了统一的接口

不好的地方在于:

  • 每个观察者都要去实现接口
  • 如果触发的时候要传递数据、就只能修改接口中的定义、并且还要修改BaseEvent中的update方法

2、看Yii2.0中event的实现方式

精简版:

  • 只有事件绑定和触发
  • 只有对象级别的绑定和触发(没有类级别的)
//所有想要使用事件功能的类都要继承
class Components
{
    //保存所有的时间
    private $_events = [];
    //绑定事件
    public function on($eventName, $handler, $data)
    {
        $this->_events[$eventName][] = [$handler, $data]; 
    }
    //触发事件
    public function trigger($eventName, $event=null)
    {
        foreach($this->_events[$eventName] as $handler){
            call_user_func($handler[0], $handler[1]);
        }
    }
}
//邮件类 负责发送邮件(相当于一个观察者)
class Email 
{
    public function send($data)
    {
        echo 'email send '.$data;
        echo '<br />';
    }
}
//短信 负责发送短信(相当于一个观察者)
class ShortMessage
{
    public fucntion send($data)
    {
        echo 'short message send '.$data;
        echo '<br />';
    }
}
//评论类 必须继承自Components
class Comment extends Components
{
    const EVENT_SEND_MESSAGE = 'send';
    //保存成功触发发送通知消息事件
    public function save()
    {
        echo 'comment save success';
        echo '<br />';
        $this->trigger(self::EVENT_SEND_MESSAGE);
    }  
}
$comment = new Comment();

$emailHandler = [new Email(), 'send'];
$smsHandler   = [new ShortMessage(), 'send'];
//注册两个事件
$comment->on(Comment::EVENT_SEND_MESSAGE, $emailHandler, 'for comment.');
$comment->on(Comment::EVENT_SEND_MESSAGE, $smsHandler, 'for comment.');
//保存评论
$comment->save();

//输出
comment save success
email send for comment.
short message send for comment.

对比第一种观察者模式的实现:

  • 不需要每个观察者都实现统一的接口
  • 利用call_user_func可以之间传递数组:包括对象实例和对应的方法

改进:改进trigger方法

  • 触发时传递数据给观察者
//添加Event类
class Event
{
    public $data = null;
}
//修改trigger方法
public function trigger($eventName, $event=null)
{
    if(is_null($event)){
        $event = new Event();
    }
    foreach($this->_events[$eventName] as $handler){
        $event->data = $handler[1];
        call_user_func($handler[0], $event);
    }
}

修改之后传递给事件 处理者。定义统一的一个event之后,所有的事件处理者需要继承此类,call_user_func传递的不再是data了,而是一个event对象

所以Yii框架事件两种传递数据的方式:

  • 在on绑定的时候
  • 在trigger的时候

最终trigger传递的事实上是一个event,on传递的参数变成了event的data的属性

原文链接:http://www.cnblogs.com/skyfynn/p/8891008.html

原文地址:https://www.cnblogs.com/lovezbs/p/13909321.html