PHP設計模式之觀察者模式

概念

觀察者模式屬於行爲模式,是定義對象間的一種一對多的依賴關係,以便當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並自動刷新。php

當一個對象狀態發生改變後,會影響到其餘幾個對象的改變,這時候能夠用觀察者模式。html

觀察者模式符合接口隔離原則,實現了對象之間的鬆散耦合。ide

別名

  • 發佈-訂閱<Subscribe>模式測試

  • 模型-視圖<View>模式this

  • 源-收聽者<Listener>模式spa

  • 從屬者模式.net

角色

  1. 抽象主題(Subject):它把全部觀察者對象的引用保存到一個彙集裏,每一個主題均可以有任何數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者對象。code

  2. 具體主題(ConcreteSubject):將有關狀態存入具體觀察者對象;在具體主題內部狀態改變時,給全部登記過的觀察者發出通知。server

  3. 抽象觀察者(Observer):爲全部的具體觀察者定義一個接口,在獲得主題通知時更新本身。htm

  4. 具體觀察者(ConcreteObserver):實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題狀態協調。

UML圖

clipboard.png

代碼

示例代碼

在PHP SPL中已經提供SplSubject和SqlOberver接口,源碼以下:

/**
 * The <b>SplSubject</b> interface is used alongside
 * <b>SplObserver</b> to implement the Observer Design Pattern.
 * @link http://php.net/manual/en/class.splsubject.php
 */
interface SplSubject  {

        /**
         * Attach an SplObserver
         * @link http://php.net/manual/en/splsubject.attach.php
         * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to attach.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function attach (SplObserver $observer);

        /**
         * Detach an observer
         * @link http://php.net/manual/en/splsubject.detach.php
         * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to detach.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function detach (SplObserver $observer);

        /**
         * Notify an observer
         * @link http://php.net/manual/en/splsubject.notify.php
         * @return void 
         * @since 5.1.0
         */
        public function notify ();

}

/**
 * The <b>SplObserver</b> interface is used alongside
 * <b>SplSubject</b> to implement the Observer Design Pattern.
 * @link http://php.net/manual/en/class.splobserver.php
 */
interface SplObserver  {

        /**
         * Receive update from subject
         * @link http://php.net/manual/en/splobserver.update.php
         * @param SplSubject $subject <p>
     * The <b>SplSubject</b> notifying the observer of an update.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function update (SplSubject $subject);

}

下面咱們根據這兩個spl接口,寫本身的代碼:

<?php
header('Content-type:text/html;charset=utf-8');

/**
 * Class Subject 主題
 */
class Subject implements SplSubject
{
    private $_observers = [];

    /**
     * 實現添加觀察者方法
     *
     * @param SplObserver $observer
     */
    public function attach(SplObserver $observer)
    {
        if (!in_array($observer, $this->_observers)) {
            $this->_observers[] = $observer;
        }
    }

    /**
     * 實現移除觀察者方法
     *
     * @param SplObserver $observer
     */
    public function detach(SplObserver $observer)
    {
        if (false !== ($index = array_search($observer, $this->_observers))) {
            unset($this->_observers[$index]);
        }
    }

    /**
     * 實現提示信息方法
     */
    public function notify()
    {
        foreach ($this->_observers as $observer) {
            $observer->update($this);
        }
    }

    /**
     * 設置數量
     *
     * @param $count
     */
    public function setCount($count)
    {
        echo "數據量加" . $count . '<br>';
    }

    /**
     * 設置積分
     *
     * @param $integral
     */
    public function setIntegral($integral)
    {
        echo "積份量加" . $integral . '<br>';
    }

}

/**
 * Class Observer1 觀察者一
 */
class Observer1 implements SplObserver
{
    public function update(SplSubject $subject)
    {
        $subject->setCount(10);
    }
}

/**
 * Class Observer2 觀察者二
 */
class Observer2 implements SplObserver
{
    public function update(SplSubject $subject)
    {
        $subject->setIntegral(10);
    }
}


/**
 * Class Client 客戶端
 */
class Client
{
    /**
     * 測試方法
     */
    public static function test()
    {
        // 初始化主題
        $subject = new Subject();
        // 初始化觀察者一
        $observer1 = new Observer1();
        // 初始化觀察者二
        $observer2 = new Observer2();
        // 添加觀察者一
        $subject->attach($observer1);
        // 添加觀察者二
        $subject->attach($observer2);
        // 消息提示
        $subject->notify();//輸出:數據量加1 積份量加10
        // 移除觀察者一
        $subject->detach($observer1);
        // 消息提示
        $subject->notify();//輸出:數據量加1 積份量加10 積份量加10
    }
}

// 執行測試
Client::test();

運行結果

數據量加10
積份量加10
積份量加10

優勢和缺點

優勢

  1. 觀察者和主題之間的耦合度較小;

  2. 支持廣播通訊;

缺點

  1. 因爲觀察者並不知道其它觀察者的存在,它可能對改變目標的最終代價一無所知。這可能會引發意外的更新。

適用場景

  1. 當一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面。

  2. 當對一個對象的改變須要同時改變其它對象,而不知道具體有多少個對象待改變。

  3. 當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換句話說,你不但願這些對象是緊密耦合的。

相關文章
相關標籤/搜索