1.初識觀察者模式
生活中大家一定遇到過這樣的情況:某一件事情的狀態改變,會相應的引起其他相關的變化。身在武林就說說武林的事吧,就說最近熱播的電視劇《天涯明月刀》,看過這么一集,劇情大概是:“了因師太”和“國介方丈”假傳向盟主的口令邀請武林各大門派到俠客山庄開“鏟雪大會”,目的是鏟除他們眼中的武林敗類“傅紅雪”。其實這就是一個觀察者模式的典型例子啦。
被觀察者是:“了因師太”和“國介方丈”。(即:他們狀態的改變的會影響其他的事情的變化)
觀察者是:武當派等等其他武林派別。(即:受被觀察者狀態改變的影響作出相應的變化)
GoF對觀察者模式定義:定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
實際上,觀察者模式又被成為發布/訂閱模式,在這種模式中,一個目標物件(被觀察者)管理所有相依於它的相關物件(觀察者),並且在目標物件的狀態改變時主動發出通知。這通常透過使用各相關物件所提供的方法來實現,觀察者模式模式通常被用來作事件處理系統。
2.觀察者模式實現
我們不妨在舉一個大家比較熟悉的例子:用戶界面可以作為一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化后,就顯示在界面上。
現在大家應該對觀察者模式理解的差不多了,那么觀察者模式怎么實現呢? 觀察者模式有很多實現方式,從根本上說,該模式必須包含兩個角色:觀察者和被觀察對象。在剛才的例子中,業務數據是被觀察對象,用戶界面是觀察者。觀察者和被觀察者之間存在“觀察”的邏輯關聯,當被觀察者發生改變的時候,觀察者就會觀察到這樣的變化,並且做出相應的響應。如果在用戶界面、業務數據之間使用這樣的觀察過程,可以確保界面和數據之間划清界限,假定應用程序的需求發生變化,需要修改界面的表現,只需要重新構建一個用戶界面,業務數據不需要發生變化。這樣一說,似乎我們要實現觀察者模式就要最少有:被觀察者類和觀察者類。
在實現觀察者模式的多種形式中,比較直觀的一種是使用一種“注冊—通知—撤銷注冊”的形式。具體實現過程的關鍵步驟如下:
注冊:在被觀察者對象中,放置一個容器來保存相關的觀察者對象。
通知:一旦被觀察者對象中的狀態發生改變,則通知容器中所有觀察者對象做出相應的變化。
撤銷注冊:將被觀察者對象容器中某個要撤銷的觀察者對象刪除。
注:實際上這種實現方式不僅僅要包含被觀察者類和觀察者類。因為觀察者對象將自己注冊到被觀察者的容器中時,被觀察者不應該過問觀察者的具體類型,而是應該使用觀察者的接口。這樣的優點是:假定程序中還有別的觀察者,那么只要這個觀察者也是相同的接口實現即可。一個被觀察者可以對應多個觀察者,當被觀察者發生變化的時候,他可以將消息一通知給所有的觀察者。基於接口,而不是具體的實現,這一點為程序提供了更大的靈活性。也就是說還要包含所以觀察者類的基類。這樣便於擴展。還是看看觀察者模式的類圖吧:
其中Subject是抽象被觀察者,Observer是抽象的觀察者類,Attach是向被觀察者容器中加入觀察者對象,Detach是從被觀察者容器中刪除相關的觀察者對象,NOtify是具體的通知操作,通知所以容器中的觀察者做出相應的操作(觀察者中的Update)。這樣的設計更符合依賴倒置原則,並且減小了系統的耦合性。現在寫代碼已經非常簡單了吧。但是這樣其實還是有問題的。
3.觀察者模式 VS 委托
我們再來對上面的觀察者模式進行分析,可以發現,這種實現是有不足之處的:
(1)並沒有完全的解除系統的耦合。
(2)所有觀察者要完成的操作未必是一致的,即:未必都是Update操作,不同的觀察者所要完成的事情未必一致。
對於以上存在的兩個問題,通過委托可以很少的解決。那么什么事委托呢?委托實際上和C++中的函數指針比較相似,實際上委托是一種引用方法的類型,一旦委托分配了方法,委托將與該方法具有完全相同的行為。委托方法的使用可以像其他任何方法一樣,具有參數和返回值,委托可以看成是函數的抽象,是函數的類,委托的實例將代表具體的函數,而且比較重要的是,委托可以搭載多個方法,所有方法別依次喚起。
通過對於委托的介紹,我們想是不是可以通過委托來代替原來的被觀察者類中的容器呢?這樣就減小了耦合,因為被觀察者中不需要保存觀察者對象了。實際上是可以的,只需要在被觀察者類聲明一個委托,然后再客戶端處,動態的為被觀察者對象中的委托賦值,賦的值就是當被觀察者狀態發生改變時,觀察者對象所要進行的操作,這樣一來,即使觀察者對象要完成的操作不一樣,也可以很好的解決啦。我只能說,委托真是好啊~~~
4.使用觀察者模式的場合和好處
以下情況使可以考慮使用觀察者模式:
(1)當一個抽象模型有兩個方面,其中一個方面依賴另一個方面,這時使用觀察者模式可以將這兩個者封裝在獨立的對象中使他們各自獨立的改變和復用。
(2)當一個對象的改變要影響到其他對象,而且不知道有多少的對象需要改變。
(3)當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不希望這些對象是緊密耦合的。
那么使用觀察者模式有什么好處呢?主要是降低了系統中各個對象之間的耦合性,使得系統易於擴展。總的來說:
(1)使用面向對象的抽象,觀察者模式使得我們可以獨立地改變目標與觀察者,從而使二者之間的依賴關系達到松耦合。
(2)目標發送通知時,無需指定觀察者,通知(可以攜帶通知信息作為參數)會自動傳播。觀察者自己決定是否需要訂閱通知。目標對象對此一無所知。
(3)在C#中的Event。委托充當了抽象的Observer接口,而提供事件的對象充當了目標對象,委托是比抽象Observer接口更為松耦合的設計。
學習中的一點總結,歡迎拍磚哦^^