php設計模式(4) 觀察者模式(附加 不聽話的觀察者)

php設計模式(4) 觀察者模式(附加 不聽話的觀察者)

概述

觀察者模式要求每個觀察者都實現(xiàn)指定的接口,但是如果有其他原因導致不能直接注冊,這時候我們應該使用反向注冊

代碼實現(xiàn)

目標 和 觀察者(使用了 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);//觀察者進行更新狀態(tài)
}

具體目標(被觀察者)

class News implements SplSubject
{
    public $pid       = 0;
    public $observers = [];

    public function __construct($pid)
    {
        $this->pid = $pid;

        /**
         * 實現(xiàn)php 自動加載
         */
        spl_autoload_register([$this, 'loadRegister']);
        /**
         * 這里是加載 Plug 目錄下 所有插件
         */
        $this->autoRegister();
    }

    /**
     * 實現(xiàn)具體自動加載步驟
     *
     * 這里沒有 遵循 psr 規(guī)范 強烈不建議
     *
     * 我這么寫只是方便
     *
     * @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)]);
        }
    }

    /**
     * 當狀態(tài)改變時 通知所有觀察者
     */
    public function notify()
    {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    /**
     * 相當于改變狀態(tài)的方法 改變狀態(tài)后調用 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
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Observer 含義 定義對象之間的一對多依賴關系,以便當一個對象更改狀態(tài)時,它的所有依賴關系將被自動通知和更新...
    kylesean閱讀 1,335評論 0 1
  • php設計模式(4) 觀察者模式 概述 問題 解決 適用性(在以下任意情況均可使用) 當一個抽象模型有兩個方面,一...
    小楊不是小羊閱讀 630評論 0 2
  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 4,094評論 1 15
  • Welcome 目前網(wǎng)絡上充斥著大量的陳舊信息,讓PHP新手誤入歧途,傳播著錯誤的實踐和糟糕的代碼,這必須得到糾正...
    layjoy閱讀 21,848評論 7 118
  • 窗前,那一抹昏黃的燈光 姨奶八十壽誕在即,大家相約一起為她老人家過一個熱熱鬧鬧的壽宴。 姨奶是奶奶的親妹妹,小時候...
    璞玉菲嘜閱讀 863評論 3 11

友情鏈接更多精彩內(nèi)容