PHP設計模式之觀察者模式

PHP設計模式之觀察者模式

觀察者,貌似在不少科幻做品中都會有這個角色的出現。好比我很喜歡的一部美劇《危機邊緣》,在這個劇集中,觀察者不停的穿越時空記錄着各類各樣的人或事。可是,設計模式中的觀察者可不僅是站在邊上看哦,這裏的觀察者是針對主體發生的狀態改變來作出對應的動做。php

Gof類圖及解釋

GoF定義:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新git

GoF類圖github

適配器方法結構類圖-繼承式

代碼實現數據庫

interface Observer {
    public function update(Subject $subject): void;
}
複製代碼

觀察者的抽象接口,沒啥可說的吧,就是讓你實現一個具體的Update就能夠了編程

class ConcreteObserver implements Observer {
    private $observerState = '';
    function update(Subject $subject): void {
        $this->observerState = $subject->getState();
        echo '執行觀察者操做!當前狀態:' . $this->observerState;
    }
}
複製代碼

具體的觀察者,實現update()方法,這裏咱們拿到了Subject類,從而能夠得到其中的狀態設計模式

class Subject {
    private $observers = [];
    private $stateNow = '';
    public function attach(Observer $observer): void {
        array_push($this->observers, $observer);
    }
    public function detach(Observer $observer): void {
        $position = 0;
        foreach ($this->observers as $ob) {
            if ($ob == $observer) {
                array_splice($this->observers, ($position), 1);
            }
            ++$position;
        }
    }
    public function notify(): void {
        foreach ($this->observers as $ob) {
            $ob->update($this);
        }
    }
}
複製代碼

Subject父類,維護一個觀察者數組,而後有添加、刪除以及循環遍歷這個數組的方法,目的是可以方便的管理全部的觀察者數組

class ConcreteSubject extends Subject{
    public function setState($state) {
        $this->stateNow = $state;
        $this->notify();
    }

    public function getState() {
        return $this->stateNow;
    }
}
複製代碼

Subject的實現類,只是更新了狀態,在這個狀態發生改變的時候,調用觀察者遍歷的方法進行全部觀察的update()操做編程語言

  • 觀察者,其實就是自身作了一個更新(update),而Subject,能夠批量的執行觀察者,請注意,咱們不須要去修改目標類中的任何代碼,只須要從外部添加就能夠了,因此就讓目標和觀察者解耦互相之間不用關心對方的狀況了
  • 觀察者能夠記錄目標的狀態,也能夠不用記錄,好比咱們發完短信後的數據庫更新或者插入操做,只有短信接口發送成功後咱們再修改短信數據的狀態就能夠了,不必定徹底須要將目標的發送狀態傳送給觀察者
  • 當一個類在發生改變時,不知道可能會對其餘多少類產生影響,這個時候觀察者很是有用
  • 觀察者模式中仍是存在着耦合,那就是目標類中有一個觀察者對象列表,若是觀察者沒有實現update()方法,那麼就會出現問題

接着拿咱們的手機工廠說事兒,此次好嘛,被一幫山寨機盯上了(觀察者),我出什麼功能(狀態更新),他們就對應的出同樣的功能(更新),並且還在個人基礎上作了更多的東西,美其名曰:微創新!你說氣人不氣人。好吧,我也派出了一幫市場調查人員(觀察者),去幫我觀察別人家的手機都出了什麼功能(狀態更新),而後咱們也照搬過來搞點微創新,你們共同進步嘛!!this

完整代碼:github.com/zhangyue050…spa

實例

此次咱們從訂單提及,不過仍是有短信發送的事兒。當通常的電商平臺有人下單以後,須要作的事情很是多,好比修改庫存、發送短信或者推送告訴商家有人下單了,告訴買家下單成功了,支付成功了。總之就是一件事情的發生會致使各類事件的產生。其實,這裏就引出了另外一個很是出名的模式訂閱發佈模式。這個模式能夠說是觀察者的升級模式,這個系列的文章不會細講,可是你們能夠去看看Laravel中的發佈訂閱事件監聽方面的內容。

訂單售出類圖

訂單售出觀察者模式

完整源碼:github.com/zhangyue050…

interface Observer {
    public function update($obj);
}

class Message implements Observer {
    //....

    function update($obj) {
        echo '發送新訂單短信(' . $obj->mobile . ')通知給商家!';
    }

    //....
}

class Goods implements Observer {
    //....

    public function update($obj) {
        echo '修改商品' . $obj->goodsId . '的庫存!';
    }

    //....
}

class Order {
    private $observers = [];
    public function attach($ob) {
        $this->observers[] = $ob;
    }

    public function detach($ob) {
        $position = 0;
        foreach ($this->observers as $ob) {
            if ($ob == $observer) {
                array_splice($this->observers, ($position), 1);
            }
            ++$position;
        }
    }
    public function notify($obj) {
        foreach ($this->observers as $ob) {
            $ob->update($obj);
        }
    }
    public function sale() {
        // 商品賣掉了
        // ....
        $obj = new stdClass();
        $obj->mobile = '13888888888';
        $obj->goodsId = 'Order11111111';
        $this->notify($obj);
    }
}

$message = new Message();
$goods = new Goods();
$order = new Order();
$order->attach($message);
$order->attach($goods);

// 訂單賣出了!!
$order->sale();

複製代碼

說明

  • 咱們沒有徹底的遵照GoF類圖,雖然說GoF是聖經,但也並非咱們必需要徹底遵照的,咱們能夠針對具體的業務狀況進行合適的裁剪使用
  • 訂單狀態經過sale()方法產生變化後,直接調用notify方法進行觀察者的調用
  • 發短信、發推送均可以拆開由一個一個的觀察者來實現,這些觀察者不必定只有這一個方法,但只要實現共同的接口就能夠了
  • 商品庫存和消息發送其實就是兩個自己徹底不沾邊的類,但它們只須要實現同樣的接口就好啦
  • PHP的SPL擴展中已經爲咱們準備好了一套觀察者接口,你們能夠試試哦,使用原生支持的觀察者模式能省很多事兒呢!

SPL擴展實現觀察者模式-完整源碼:github.com/zhangyue050…

下期看點

循環是編程語言的一個亮點,由於這個能力讓編程語言作出來的軟件能夠替代人們去作不少重複的勞動。一說到這裏,有的人立刻就會想到,莫非咱們下次講的就是迭代器模式?拭目以待吧!

相關文章
相關標籤/搜索