觀察者模式、事件通知、訊息佇列三者區別
阿新 • • 發佈:2022-05-12
觀察者模式、事件通知、訊息佇列三者有類似,都有回撥函式註冊,通知呼叫的設計,容易混淆。
簡述和區別
- 觀察者模式:被觀察物件狀態改變,所有觀察它的物件得到通知。也稱訂閱模式,英文Observer。
被觀察者不依賴觀察者,通過依賴注入達到控制反轉。 - 事件通知:事件發生後,通知所有關心這個事件的物件。
與觀察者模式對比,可理解成所有物件都只依賴事件系統。一半物件觀察事件系統,等待特定通知;一半物件狀態變化就通過事件系統發出事件。
觀察者也不依賴被觀察物件,他只關心事件,不需要到被觀察物件那兒註冊自己。
被觀察者也只是普通物件,狀態改變,通過事件系統發出事件就行了。 - 訊息佇列:將訊息排成佇列,逐步分發通知。
與事件通知對比,可理解成事件不是立即通知,而是儲存到佇列裡,稍後通知。
這個可以達到時間解耦的效果。Windows的訊息迴圈就是一個應用。多執行緒情況下,訊息佇列優先於事件系統。
觀察者模式
以上課鈴聲為例子。上課鈴聲響,同學們回教室。
1. 簡單寫法
class 上課鈴{ function 響() for 學生 in 學生們 do 學生->回教室() end end }
這樣寫有問題:
- 上課鈴主動通知學生回教室,依賴關係反了。
- 上課鈴響,老師要來上課,這個也得上課鈴通知,上課鈴管的東西太多了。
2. 輪詢
class 學生{ function update() if 上課玲響 then 回教室() end end }
這樣上課鈴只管按時響就行了,也有問題:
- 學生的update會越來越複雜,學生還有很多其他事情要做呢。
- update太耗時了,學生們,要精神緊張地仔細停玲聲有沒有響起。
3. 用觀察者模式
class 上課鈴: Subject{ function 響() NotifyObservers() end } class 學生: Observer{ function init() 上課玲->AddObserver(this.回教室) end function 回教室() ... end function un_init() 上課玲->RemoveObserver(this.回教室) end }
這樣,上課鈴只要響的時候發個通知,學生們就等通知好了。老師也類似,等通知就行了。
小結
實際就是註冊個回撥函式,完美的將觀察物件和被觀察物件分離。
個人理解:依賴注入,控制反轉。觀察者依賴被觀察者,而不是被觀察者依賴觀察者。
事件系統
觀察者模式有兩個問題:
- 觀察者要獲得被觀察物件,然後才能註冊。
有時只是要知道某個事件發生了而已,類似網路初始化好了的事件,並不需要獲得網路管理物件。 - 觀察者和被觀察者要繼承物件的,在單繼承體系裡,這是很昂貴的一件事。
上課鈴的例子裡,學生只關心鈴聲,不關心上課鈴這個物體。
用事件模式就可以換個寫法
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 }
小結
事件通知系統用的很廣泛的。很多程式碼會有個EventDispatcher
、EventControl
之類的類。
特別是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們在主執行緒裡。傳送訊息的可以在其他執行緒裡。
簡單總結
需要分層解耦就用事件通知系統。
需要時間解耦就用訊息佇列。
搜尋
複製