PHP 設計模式之觀察者模式

echo_dump發表於2020-06-13

PHP之觀察者模式

  • 觀察者模式在PHP裡面是有已經定義好的兩個介面檔案,我們只需要繼承它並實現它,那麼我們的觀察者模式就已經算是實現了。
  1. 首先觀察者模式,顧名思義,就是需要有一個觀察者和被觀察者,被觀察的物件發生了變化,可以及時通知所有觀察這個物件變化的物件

  2. 在PHP種預定義的觀察者: \SplObserver觀察者定義了一個介面:

interface SplObserver
{
    public function update(SplSubject $subject);
}
  1. 在PHP種預定義的被觀察者:\SplSubject,這個被觀察者定義了三個介面方法
interface SplSubject
{
    public function attach(SplObserver $observer);

    public function detach(SplObserver $observer);

    public function notfiy();


}
  1. 很明顯,在觀察者方法裡,如果有變化了就呼叫update()方法; 在被觀察裡,第一個方法attach(),新增一個被觀察者到一個自己維護的資料結構中,
    這個結構可以是PHP的陣列array, 可以是一個集合,可以是一個has表,具體的怎麼實現,都可以自己操作。detach()這個方法就是從之維護的一個結
    構中,刪掉這個被觀察者。最後一個核心的方法就是notfiy,它是從之前的資料儲存結構中,遍歷所有的觀察者,呼叫觀察者裡的方法,通知它需要做事情了。

  2. 之前第四步說的儲存註冊觀察者的資料結構,可以是一個陣列,可以是一個集合,同時還可以是PHP自己實現的一個\SplObjectStorage的一個管理類
    SplObjectStorage 在裡面它實現了很多方法,包括但不限於attach(), detach()
    是一個很強大的類,可以幫我們做這些事情。如下:

class SplObjectStorage implements \Countable, \Iterator, \Serializable, \ArrayAccess {

    public function attach ($object, $data = null) {}
    public function detach ($object) {}
    public function contains ($object) {}
    public function addAll ($storage) {}
    public function removeAll ($storage) {}
    public function removeAllExcept ($storage) {}
    public function getInfo () {}
    public function setInfo ($data) {}

    /**
     * @inheritDoc
     */
    public function current()
    {
        // TODO: Implement current() method.
    }

    /**
     * @inheritDoc
     */
    public function next()
    {
        // TODO: Implement next() method.
    }

    /**
     * @inheritDoc
     */
    public function key()
    {
        // TODO: Implement key() method.
    }

    /**
     * @inheritDoc
     */
    public function valid()
    {
        // TODO: Implement valid() method.
    }

    /**
     * @inheritDoc
     */
    public function rewind()
    {
        // TODO: Implement rewind() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetExists($offset)
    {
        // TODO: Implement offsetExists() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetGet($offset)
    {
        // TODO: Implement offsetGet() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetSet($offset, $value)
    {
        // TODO: Implement offsetSet() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetUnset($offset)
    {
        // TODO: Implement offsetUnset() method.
    }

    /**
     * @inheritDoc
     */
    public function serialize()
    {
        // TODO: Implement serialize() method.
    }

    /**
     * @inheritDoc
     */
    public function unserialize($serialized)
    {
        // TODO: Implement unserialize() method.
    }

    /**
     * @inheritDoc
     */
    public function count()
    {
        // TODO: Implement count() method.
    }
}
  1. 使用一個觀者者模式解決我們業務上的一個邏輯,如下:純屬虛構。
    假如現在有一個天氣預報的平臺,它是一個權威的檢測和釋出天氣的平臺,它得到最新的天氣後,會把最新的天氣情況轉送給給大媒體平臺,這樣我們不管在那個
    媒體平臺都可以看到最新的天氣情況,假設現在有新浪天氣,百度天氣,騰訊天氣,墨跡天氣。。。假設是有這四家來得到天氣的變化情況,然後免費更新。那麼
    現在這個權威的天氣檢測平臺就是一個被觀察者,而那四家媒體平臺就是觀察者。

  2. 現在建立一個被觀察者媒體平臺

class Weather implements \SplSubject
{

    /**
    *  管理所有觀察者
    * @var SplObjectStorage 
     */
    private $observers;

    // 最新的天氣情況
    private $email;

    public function __construct()
    {
        $this->observers = new \SplObjectStorage();
    }

    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @inheritDoc 註冊觀察者
     */
    public function attach(SplObserver $observer)
    {
        // TODO: Implement attach() method.
        $this->observers->attach($observer);
    }

    /**
     * @inheritDoc 登出觀察者
     */
    public function detach(SplObserver $observer)
    {
        // TODO: Implement detach() method.
        $this->observers->detach($observer);
    }

    // 天氣改變後,通知所有的觀察者
    public function changeWeather(string $email)
    {
        $this->setEmail($email)->notify();
    }

    /**
     * @inheritDoc 通知所有的觀察者
     */
    public function notify()
    {
        // TODO: Implement notify() method.
        foreach ($this->observers as $observer) {
            $observer->update($this);//呼叫觀察的update方法
        }
    }
}
  1. 建立四個觀察者
class Baidu implements \SplObserver
{

    /**
     * @inheritDoc
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        echo '給百度發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
    }
}

class Tencent implements \SplObserver
{

    /**
     * @inheritDoc
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        echo '給騰訊發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
    }
}

class MoJi implements \SplObserver
{

    /**
     * @inheritDoc
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        echo '給墨跡發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
    }
}

class Sina implements \SplObserver
{

    /**
     * @inheritDoc
     */
    public function update(SplSubject $subject)
    {
        // TODO: Implement update() method.
        echo '給新浪發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
    }
}
  1. 客戶端測試呼叫
class Client
{
    public function __construct(string $email)
    {
        $weather = new Weather();
        $baidu = new Baidu();
        $sina = new Sina();
        $moji = new MoJi();
        $tencent = new Tencent();

        $weather->attach($baidu);
        $weather->attach($sina);
        $weather->attach($moji);
        $weather->attach($tencent);
        $weather->changeWeather($email);
    }
}

new Client('今天的天氣溫度是 36.6攝氏度,注意高溫防曬');
new Client('今天的天氣溫度是 40.0攝氏度,注意高溫防曬');
new Client('今天的天氣溫度是 22.0攝氏度,溫度剛剛好');
  1. 當我們天氣變化的時候,就直接呼叫changeWeather()方法,這樣四家媒體平臺就都得到最新的天氣情況了
本作品採用《CC 協議》,轉載必須註明作者和本文連結
LIYi ---- github地址

相關文章