美文网首页
php设计模式(4) 观察者模式(附加 不听话的观察者)

php设计模式(4) 观察者模式(附加 不听话的观察者)

作者: 小杨不是小羊 | 来源:发表于2018-08-17 17:19 被阅读0次

    php设计模式(4) 观察者模式(附加 不听话的观察者)

    概述

    观察者模式要求每个观察者都实现指定的接口,但是如果有其他原因导致不能直接注册,这时候我们应该使用反向注册
    

    代码实现

    目标 和 观察者(使用了 php spl 提供的接口)

    <?php
    
    interface SplSubject{
          public function attach(SplObserver $observer);//注册观察者
          public function detach(SplObserver $observer);//释放观察者
          public function notify();//通知所有注册的观察者
    }
    interface SplObserver{
          public function update(SplSubject $subject);//观察者进行更新状态
    }
    

    具体目标(被观察者)

    class News implements SplSubject
    {
        public $pid       = 0;
        public $observers = [];
    
        public function __construct($pid)
        {
            $this->pid = $pid;
    
            /**
             * 实现php 自动加载
             */
            spl_autoload_register([$this, 'loadRegister']);
            /**
             * 这里是加载 Plug 目录下 所有插件
             */
            $this->autoRegister();
        }
    
        /**
         * 实现具体自动加载步骤
         *
         * 这里没有 遵循 psr 规范 强烈不建议
         *
         * 我这么写只是方便
         *
         * @param $class_name
         */
        public function loadRegister($class_name)
        {
            $class_name = str_replace("\\", "/", $class_name);
            include dirname(__DIR__) . '/' . $class_name . '.php';
        }
    
        /**
         * 加载插件方法
         *
         * 这里是为了方便 一次性加载 Plug 下所有插件
         *
         * 并注册观察者
         */
        public function autoRegister()
        {
            $path      = __DIR__ . "/plug/";
            $directory = dir($path);
            while ($file = $directory->read()) {
                if ($file == '.' || $file == "..") {
                    continue;
                }
    
                $class_name = basename($file, '.php');//NewsClick
                /**
                 * 这里是因为有命名空间 所以要这么写 完全限定名称
                 *
                 * 关于更多命名空间直接参考手册 很详细
                 */
                $str        = '\\News\\Plug\\' . $class_name;
                // 在实例化的时候 直接把当前类传给要实例的目标 如果目标需要可以自行注册
                $object     = new $str($this);
    
                //判断是否是 SplObserver 的子类 如果是直接注册为观察者
                if (is_subclass_of($object, 'SplObserver', true)) {
                    $this->attach($object);
                }
            }
        }
    
        /**
         * 添加一个观察者
         *
         * @param \SplObserver $observer
         */
        public function attach(SplObserver $observer)
        {
            $this->observers[strval($observer)] = $observer;
        }
    
        /**
         * 删除一个观察者
         *
         * @param \SplObserver $observer
         */
        public function detach(SplObserver $observer)
        {
            if (array_search($observer, $this->observers, true)) {
                unset($this->observers[strval($observer)]);
            }
        }
    
        /**
         * 当状态改变时 通知所有观察者
         */
        public function notify()
        {
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    
        /**
         * 相当于改变状态的方法 改变状态后调用 notify
         */
        public function read()
        {
            echo "阅读了 product id " . $this->pid . " 的文章";
            $this->notify();
        }
    }
    

    具体观察者

    // 这类继承 SplObserver 目标(被观察者)会直接将其注册为观察者
    class NewsClick implements SplObserver
    {
        /**
         * 接收到通知后你做什么由你来决定
         *
         * @param \SplSubject $subject
         */
        public function update(SplSubject $subject)
        {
            echo "<hr>";
            echo 'product ID' . $subject->pid . '点击量加1';
        }
    
        /**
         * 这个模式方法让我用了做 key 了 你随意
         *
         * @return string
         */
        public function __toString()
        {
            return "news_click";
        }
    }
    
    // 这个类没有继承 SplObserver 所以需要自己来添加观察者
    class NewsLike
    {
        public function __construct(SplSubject $news)
        {
            $news->attach(new class implements SplObserver
            {
    
                /**
                 * 接收到通知后你做什么由你来决定
                 *
                 * @param \SplSubject $subject
                 */
                public function update(SplSubject $subject)
                {
                    echo "<hr>";
                    echo 'product ID' . $subject->pid . '收藏量加1';
                }
    
                /**
                 * 这个模式方法让我用了做 key 了 你随意
                 *
                 * @return string
                 */
                public function __toString()
                {
                    return "news_like";
                }
            });
        }
    }
    

    调用

    //因为使用了自动加载 代码变少了
    use news\News;
    
    $news = new News(100);
    
    $news->read();
    

    结果

    阅读了 product id 100 的文章<hr>product ID100收藏量加1<hr>product ID100点击量加1<hr>product ID100日志加1

    相关文章

      网友评论

          本文标题:php设计模式(4) 观察者模式(附加 不听话的观察者)

          本文链接:https://www.haomeiwen.com/subject/nsetiftx.html