zoukankan      html  css  js  c++  java
  • PHP观察者模式与Yii2.0事件

    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

  • 相关阅读:
    JMeter压力测试并发测试(入门篇)
    System.currentTimeMillis()的性能问题以及解决方法
    Java日期时间API系列4-----Jdk7及以前的日期时间类的线程安全问题【转】
    使用Apache Commons Chain
    Spring中的InitializingBean接口的使用[转]
    Hash环/一致性Hash原理【转】
    ASP.NET Core 进程内模型和进程外模式
    如何运行多个同名Windows服务实例
    WPF系列 —— 控件添加依赖属性
    WPF系列——简单绑定学习
  • 原文地址:https://www.cnblogs.com/skyfynn/p/8891008.html
Copyright © 2011-2022 走看看