觀察者模式

模式定義

觀察者模式(Observer Pattern):定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並被自動更新。觀察者模式又叫做發佈-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

觀察者模式的核心在於Subject和Observer接口,Subject(主題目標)包含一個給定的狀態,觀察者“訂閱”這個主題,將主題的當前狀態通知觀察者,每次給定狀態改變時所有觀察者都會得到通知。

發生改變的對象稱爲觀察目標,而被通知的對象稱爲觀察者,一個觀察目標可以對應多個觀察者,而且這些觀察者之間沒有相互聯繫,可以根據需要增加和刪除觀察者,使得系統更易於擴展。

模式結構說明

  • Subject 目標抽象類
  • ConcreteSubject 具體目標
  • Observer 觀察者抽象類
  • ConcreteObserver 具體觀察者

應用舉例

比如在設置用戶(主題)的狀態後要分別發送當前的狀態描述信息給用戶的郵箱和手機,我們可以使用兩個觀察者訂閱用戶的狀態,一旦設置狀態後主題就會通知的訂閱了狀態改變的觀察者,在兩個觀察者裏面我們可以分別來實現發送郵件信息和短信信息的功能。

1.抽象目標類

 

abstract class Subject
{
    protected $stateNow;
    protected $observers = [];

    public function attach(Observer $observer)
    {
        array_push($this->observers, $observer);
    }

    public function detach(Observer $ob)
    {
        $pos = 0;
        foreach ($this->observers as $viewer) {
            if ($viewer == $ob) {
                array_splice($this->observers, $pos, 1);
            }
            $pos++;
        }
    }

    public function notify()
    {
        foreach ($this->observers as $viewer) {
            $viewer->update($this);
        }
    }
}

 

在抽象類中attach detach 和notify都是具體方法,這些是繼承才能使用的方法,將由Subject的子類使用。

2.具體目標類

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

    public function getState()
    {
        return $this->stateNow;
    }
}

 

3.抽象觀察者

abstract class Observer
{
    abstract public function update(Subject $subject);
}

在抽象觀察者中,抽象方法update等待子類爲它提供一個特定的實現。

4.具體觀察者

class ConcreteObserverDT extends Observer
{
    private $currentState;

    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();

        echo '<div style="font-size:10px;">'. $this->currentState .'</div>';
    }
}

class ConcreteObserverPhone extends Observer
{
    private $currentState;

    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();

        echo '<div style="font-size:20px;">'. $this->currentState .'</div>';
    }
}

在例子中爲了理解起來簡單,我們只是根據不同的客戶端設置了不同的內容樣式,實際應用中可以真正的調用郵件和短信服務來發送信息。

5.使用觀察者模式

class Client 
{
    public function __construct()
    {
        $sub = new ConcreteSubject();

        $obDT = new ConcreteObserverDT();
        $obPhone = new ConcreteObserverPhone();

        $sub->attach($obDT);
        $sub->attach($obPhone);
        $sub->setState('Hello World');
    }
}

$worker = new Client();

何時使用觀察者模式

  • 一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度。
  • 一個對象必須通知其他對象,而並不知道這些對象是誰。
  • 基於事件觸發機制來解耦複雜邏輯時,從整個邏輯的不同關鍵點抽象出不同的事件,主流程只需要關心最核心的邏輯並能正確地觸發事件(Subject),其餘相關功能實現由觀察者或者叫訂閱者來完成。

總結

  • 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個目標對象,當這個目標對象的狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新。

  • 模式包含四個角色:目標又稱爲主題,它是指被觀察的對象;具體目標是目標類的子類,通常它包含有經常發生改變的數據,當它的狀態發生改變時,向它的各個觀察者發出通知;觀察者將對觀察目標的改變做出反應;在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關狀態,這些狀態需要和具體目標的狀態保持一致。

  • 觀察者模式的主要優點在於可以實現表示層和數據邏輯層的分離,並在觀察目標和觀察者之間建立一個抽象的耦合,支持廣播通信;其主要缺點在於如果一個觀察目標對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間,而且如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。

原文地址https://github.com/kevinyan815/Learning_Laravel_Kernel/blob/master/articles/Observer.md

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章