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

2018-08-17  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读