一、借鑒說明
1.《Head First Design Patterns》(中文名《深入淺出設計模式》)
2.維基百科,觀察者模式,https://zh.wikipedia.org/wiki/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F
3.MSDN,event(C#參考),https://msdn.microsoft.com/zh-cn/library/8627sbea.aspx
二、觀察者模式
提供一個被觀察者(數據庫、通知中心等),多個觀察者注冊到該被觀察者上,當觀察者關心的數據改變的時候,被觀察者會通知各個觀察者。被觀察者可以將數據主動地傳給觀察者(推方式)或者觀察者在接到通知後主動向被觀察者獲取數據(拉方式)。
現如今,用戶可以通過郵箱訂閱各式各樣的周刊,而負責這些周刊的出版社會定時地將周刊發送到用戶的郵箱,這些周刊的版面都是分為兩種,一種是頭條(即出版社特別推薦給用戶的,有詳盡說明的內容),一種是鏈接群(就是一些摘要的、附有超鏈接的內容,用戶如果有興趣的話,可以進而點擊超鏈接了解詳情)。如果用戶想要每周獲得指定出版社的周刊,就需要首先在該出版社上進行注冊,如果想訂閱多個出版社的周刊,則需要在各個出版社上都進行注冊。當然,用戶都有權利取消訂閱。
這裡,用戶User就是觀察者Observer,出版社Press就是被觀察者Observable,User訂閱了某Press的周刊就是注冊register,User取消訂閱就是注銷remove,定時發送周刊就是通知notify,而頭條就是推方式push,即Press主動推送數據給User的情況,鏈接群就是拉方式pull,即接到通知後,User主動向Press獲取數據的情況。因此,Press就有了registerObserver函數(添加觀察者)、removeObserver函數(移除觀察者)、updateImportantContent函數("推方式"推送頭條)、updateHyperlink函數("拉方式"發送鏈接),User就有了notifyPush函數("推方式"獲得頭條,各個User對頭條的處理方式不盡相同)、notifyPull函數("拉方式"獲得鏈接群,各個User對鏈接群的處理方式不盡相同)。User可以調用Press的registerObserver函數訂閱周刊,調用removeObserver函數取消訂閱周刊。UML如圖所示。
如上圖所示,為了保證Press的封裝性,具體的是指updateImportantContent函數和updateHyperlink函數私有,以確保Press才有發出通知的權利(如若沒有確保封裝性,假設有一個心懷不軌的作者,只想讓自己的作品獨占整個周刊,那麼他完全可能調用updateImportantContent函數和updateHyperlink函數達成自己的目的),但是這樣,耦合度又太高了,所以筆者認為:針對C#,推薦使用event關鍵字實現觀察者模式,具體的實現見下文。
當一個對象依賴另一個對象的數據變化的時候,或者子線程異步操作完成後通知主線程的時候,可以考慮使用觀察者模式。
設計模式(C#)的相關代碼我都放在git上:https://github.com/MGKING3/DesignPatternsUseCSharp
我使用的是VS2015,是整一個項目,下載即可以用,不時更新。
如果使用不了git的話,百度雲也是可以下載的,地址:http://pan.baidu.com/s/1bp7Txuf
望互相學習,謝謝
1.封裝原則
2.多用組合(has-a),少用繼承(is-a)
3.盡量"面向接口"
4.追求"松耦合"