Laravel核心代碼學習 -- 觀察者模式

觀察者模式

Laravel的Event事件系統提供了一個簡單的觀察者模式實現,可以訂閱和監聽應用中發生的各類事件,在PHP的標準庫(SPL)裏甚至提供了三個接口SplSubject, SplObserver, SplObjectStorage來讓開發者更容易地實現觀察者模式,不過我仍是想脫離SPL提供的接口和特定編程語言來講一下如何經過面向對象程序設計來實現觀察者模式,示例是PHP代碼不過用其餘面嚮對象語言實現起來也是同樣的。git

模式定義

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

觀察者模式的核心在於Subject和Observer接口,Subject(主題目標)包含一個給定的狀態,觀察者「訂閱」這個主題,將主題的當前狀態通知觀察者,每次給定狀態改變時全部觀察者都會獲得通知。編程

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

模式結構說明

觀察者模式UML

  • 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 detachnotify都是具體方法,這些是繼承才能使用的方法,將由Subject的子類使用。學習

  1. 具體目標類
class ConcreteSubject extends Subject
{
    public function setState($state) 
    {
        $this->stateNow = $state;
        $this->notify();
    }

    public function getState()
    {
        return $this->stateNow;
    }
}
複製代碼
  1. 抽象觀察者
abstract class Observer
{
    abstract public function update(Subject $subject);
}
複製代碼

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

  1. 具體觀察者
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>';
    }
}
複製代碼

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

  1. 使用觀察者模式
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),其他相關功能實現由觀察者或者叫訂閱者來完成。

總結

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

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

  • 觀察者模式的主要優勢在於能夠實現表示層和數據邏輯層的分離,並在觀察目標和觀察者之間創建一個抽象的耦合,支持廣播通訊;其主要缺點在於若是一個觀察目標對象有不少直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間,並且若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰。

本文已經收錄在系列文章Laravel核心代碼學習裏,歡迎訪問閱讀。

相關文章
相關標籤/搜索