編程模式·觀察者模式、事件通知、消息隊列三者區別

觀察者模式、事件通知、消息隊列三者有相似,都有回調函數註冊,通知調用的設計,容易混淆。網絡

簡述和區別

  1. 觀察者模式:被觀察對象狀態改變,全部觀察它的對象獲得通知。也稱訂閱模式,英文Observer。
    被觀察者不依賴觀察者,經過依賴注入達到控制反轉。
  2. 事件通知:事件發生後,通知全部關心這個事件的對象。
    與觀察者模式對比,可理解成全部對象都只依賴事件系統。一半對象觀察事件系統,等待特定通知;一半對象狀態變化就經過事件系統發出事件。
    觀察者也不依賴被觀察對象,他只關心事件,不須要到被觀察對象那兒註冊本身。
    被觀察者也只是普通對象,狀態改變,經過事件系統發出事件就好了。
  3. 消息隊列:將消息排成隊列,逐步分發通知。
    與事件通知對比,可理解成事件不是當即通知,而是保存到隊列裏,稍後通知。
    這個能夠達到時間解耦的效果。Windows的消息循環就是一個應用。多線程狀況下,消息隊列優先於事件系統。

觀察者模式

以上課鈴聲爲例子。上課鈴聲響,同窗們回教室。多線程

1. 簡單寫法

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

這樣寫有問題:函數

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

2. 輪詢

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

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

  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
}

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

小結

實際就是註冊個回調函數,完美的將觀察對象和被觀察對象分離。
我的理解:依賴注入,控制反轉。觀察者依賴被觀察者,而不是被觀察者依賴觀察者。線程

事件系統

觀察者模式有兩個問題:設計

  1. 觀察者要得到被觀察對象,而後才能註冊。
    有時只是要知道某個事件發生了而已,相似網絡初始化好了的事件,並不須要得到網絡管理對象。
  2. 觀察者和被觀察者要繼承對象的,在單繼承體系裏,這是很昂貴的一件事。

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

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更新。
觀察者模式能夠作到,可是事件通知來實現會更加簡單。server

消息隊列

消息隊列和事件系統很像。可是消息隊列不是當即通知,而是把消息先放到隊列裏再通知。
上課鈴的例子對象

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們在主線程裏。發送消息的能夠在其餘線程裏。

簡單總結

須要分層解耦就用事件通知系統。 須要時間解耦就用消息隊列

相關文章
相關標籤/搜索