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

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

概述

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

代码实现

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

具体目标(被观察者)

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 "
"; 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 "
"; 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 的文章
product ID100收藏量加1
product ID100点击量加1
product ID100日志加1

你可能感兴趣的:(php设计模式(4) 观察者模式(附加 不听话的观察者))