1. 程式人生 > 其它 >觀察者模式、事件通知、訊息佇列三者區別

觀察者模式、事件通知、訊息佇列三者區別

觀察者模式、事件通知、訊息佇列三者有類似,都有回撥函式註冊,通知呼叫的設計,容易混淆。

簡述和區別

  1. 觀察者模式:被觀察物件狀態改變,所有觀察它的物件得到通知。也稱訂閱模式,英文Observer。
    被觀察者不依賴觀察者,通過依賴注入達到控制反轉。
  2. 事件通知:事件發生後,通知所有關心這個事件的物件。
    與觀察者模式對比,可理解成所有物件都只依賴事件系統。一半物件觀察事件系統,等待特定通知;一半物件狀態變化就通過事件系統發出事件。
    觀察者也不依賴被觀察物件,他只關心事件,不需要到被觀察物件那兒註冊自己。
    被觀察者也只是普通物件,狀態改變,通過事件系統發出事件就行了。
  3. 訊息佇列:將訊息排成佇列,逐步分發通知。
    與事件通知對比,可理解成事件不是立即通知,而是儲存到佇列裡,稍後通知。
    這個可以達到時間解耦的效果。Windows的訊息迴圈就是一個應用。多執行緒情況下,訊息佇列優先於事件系統。

觀察者模式

以上課鈴聲為例子。上課鈴聲響,同學們回教室。

1. 簡單寫法

class 上課鈴{
    function 響()
        for 學生 in 學生們 do
            學生->回教室()
        end
    end
}

這樣寫有問題:

  1. 上課鈴主動通知學生回教室,依賴關係反了。
  2. 上課鈴響,老師要來上課,這個也得上課鈴通知,上課鈴管的東西太多了。

2. 輪詢

class 學生{
    function update()
        if 上課玲響 then
            回教室()
        end
    end
}

這樣上課鈴只管按時響就行了,也有問題:

  1. 學生的update會越來越複雜,學生還有很多其他事情要做呢。
  2. update太耗時了,學生們,要精神緊張地仔細停玲聲有沒有響起。

3. 用觀察者模式

class 上課鈴: Subject{
    function 響()
        NotifyObservers()
    end
}

class 學生: Observer{
    function init()
        上課玲->AddObserver(this.回教室)
    end
    function 回教室() ... end
    function un_init()
        上課玲->RemoveObserver(this.回教室)
    end
}

這樣,上課鈴只要響的時候發個通知,學生們就等通知好了。老師也類似,等通知就行了。

小結

實際就是註冊個回撥函式,完美的將觀察物件和被觀察物件分離。
個人理解:依賴注入,控制反轉。觀察者依賴被觀察者,而不是被觀察者依賴觀察者。

事件系統

觀察者模式有兩個問題:

  1. 觀察者要獲得被觀察物件,然後才能註冊。
    有時只是要知道某個事件發生了而已,類似網路初始化好了的事件,並不需要獲得網路管理物件。
  2. 觀察者和被觀察者要繼承物件的,在單繼承體系裡,這是很昂貴的一件事。

上課鈴的例子裡,學生只關心鈴聲,不關心上課鈴這個物體。
用事件模式就可以換個寫法

class 事件系統{
    function register(事件型別, handle);
    function remove(事件型別, handle);
    function trigger(事件型別, 資料);
}

class 上課鈴{
    function 響()
        事件系統->trigger("上課鈴聲")
    end
}

class 學生{
    function init()
        事件系統->register("上課鈴聲", this->回教室)
    end
    function 回教室() ... end
    function un_init()
       事件系統->remove("上課鈴聲", this.回教室)
    end
}

小結

事件通知系統用的很廣泛的。很多程式碼會有個EventDispatcherEventControl之類的類。
特別是UI程式,當資料發生變化時通知相關UI更新。
觀察者模式可以做到,但是事件通知來實現會更加簡單。

訊息佇列

訊息佇列和事件系統很像。但是訊息佇列不是立即通知,而是把訊息先放到佇列裡再通知。
上課鈴的例子

class 訊息佇列{
    function register(訊息型別, handle);
    function remove(訊息型別, handle);
    function sendMsg(訊息);
    function process();
}

class 上課鈴{
    function 響()
        訊息佇列->sendMsg("上課鈴聲")
    end
}

class 學生{
    function init()
        訊息佇列->register("上課鈴聲", this->回教室)
    end
    function 回教室() ... end
    function un_init()
        訊息佇列->remove("上課鈴聲", this.回教室)
    end
}

main{
    while(有訊息) do
        訊息佇列->process()
    end
}

從虛擬碼也可以看出,訊息佇列和事件系統的使用基本是一樣的。如果訊息佇列不延後處理,就是事件系統了。
訊息佇列可以用於多執行緒,接受處理訊息的handle們在主執行緒裡。傳送訊息的可以在其他執行緒裡。

簡單總結

需要分層解耦就用事件通知系統。
需要時間解耦就用訊息佇列。

搜尋

複製