觀察者模式 詳解


什麼時候需要觀察者

答案是需要觀察的時候(別噴我),觀察的意思就是去看,看外界的狀態,外界如果發生了某種變化,那觀察外界的對象就執行其對應的操作,這種場景在現實應用中很常見,或者說整個社會就是在我觀察你,你觀察我這種機制下運行的。實際一點的例子:圖形用戶程序中,圖形對象需要監視後臺數據對象的變化來做響應的顯示;再舉一現在系統中常用的一個服務:短信推送服務,當用戶發出了交易請求時,就給該用戶發送一個短信驗證碼。

觀察者模式

思考:爲了實現觀察者對外界對象變化做出響應,我們應該想到兩種做法,一種是觀察者以輪詢的方式觀察某一對象,當本次觀察發現對象狀態發生改變時就做出響應(這種效率太低,因爲這意味着觀察者在外界對象變化前不能做其他的事情);第二種做法是異步通知的方式,當對象狀態發生改變時,對象會向觀察者發送消息(在代碼中體現爲它調用了觀察者的某一方法),因爲這不像進程間通信一樣可以通過中斷或者信號的方式實現,對象之間的異步通信只能通過調用彼此之間的方法。這意味着什麼?這意味着外界對象必須擁有觀察者的指針或者引用。即他們之間應該有聚合關係。

下面我們給出一個簡單的實例代碼,就以父母來觀察孩子爲場景
首先給出一個ChinaParent類的實現
  1. //觀察者  
  2. public class ChinaParent {  
  3.       
  4.     //孩子生病了他們會給孩子電話慰問  
  5.     public void callChildren(){  
  6.           
  7.         System.out.println("回來吧,帶你去看醫生");  
  8.     }  
  9. }  

然後給出Children類的實現
  1. //被觀察者  
  2. public class ChinaChildren {  
  3.       
  4.     private ChinaParent p;  
  5.       
  6.     public void gotSick(){  
  7.           
  8.         p.callChildren();  
  9.     }  
  10. }  
ChinaChildren和ChinaParent是一種聚合關係,當孩子生病的時候會主動的調用觀察這p的callChildren()方法,以表示通知了他的觀察者。
但是這種觀察者的實現方式很不好,因爲我們在具體的實現類之間建立的很強的關聯關係,我們需要對他們進行解耦,而面向對象中解耦的方法最好不過的是面向接口的設計方法了,所以我們給這兩個對象都實現某一接口
  1. public interface Parent {  
  2.       
  3.     public void callChildren();  
  4. }  

  1. public interface Children {  
  2.       
  3.     public void notifyParent();  
  4. }  
然後具體的實現類實現對應的接口
  1. //觀察者  
  2. public class ChinaParent implements Parent{  
  3.       
  4.     //孩子生病了他們會給孩子電話慰問  
  5.     public void callChildren(){  
  6.           
  7.         System.out.println("回來吧,帶你去看醫生");  
  8.     }  
  9. }  
  1. <pre code_snippet_id="267314" snippet_file_name="blog_20140331_6_1751525" name="code" class="java">//被觀察者  
  2. public class ChinaChildren implements Children{  
  3.       
  4.     private Parent p;  
  5.       
  6.     public void notifyParent() {  
  7.           
  8.         p.callChildren();  
  9.     }  
  10.       
  11.     public void setObserver(Parent p){  
  12.         this.p = p;  
  13.     }  
  14. }</pre><br>  
  15. <br>  
  16. <pre></pre>  
  17. 這樣就把孩子和具體的Parent解耦了,現在當有新類型的Parent時,我們也不用擔心爲了將新的觀察者加入到我們的系統中而修改源碼了。如:  
  1. //觀察者  
  2. public class AmericaParent implements Parent{  
  3.       
  4.     //孩子生病了他們會給孩子電話慰問  
  5.     public void callChildren(){  
  6.           
  7.         System.out.println("自己去看醫生");  
  8.     }  
  9. }  
到此爲止其實你已經掌握了觀察者模式的核心了,說白了就是用面向接口的方法將具體的觀察者和被觀察者對象解耦了,但是實際項目中我們還會發現對象往往可以由多個對象共同觀察,所以被觀察對象中應該有多個觀察者的引用
首先我們改變Children接口
  1. public interface Children {  
  2.       
  3.     public void notifyAllParents();  
  4.       
  5. }  
然後修改對應的被觀察者
  1. import java.util.ArrayList;  
  2.   
  3. //被觀察者  
  4. public class ChinaChildren implements Children{  
  5.       
  6.     private ArrayList<Parent> ps;  
  7.       
  8.     public void notifyAllParents() {  
  9.           
  10.         for(int i=0;i<ps.size();i++){  
  11.               
  12.             ps.get(i).callChildren();  
  13.               
  14.         }  
  15.           
  16.     }  
  17.       
  18.     public void addObserver(Parent p){  
  19.         ps.add(p);  
  20.     }  
  21.       
  22.     public void delObserver(Parent p){  
  23.         ps.remove(p);  
  24.     }  
  25.       
  26. }  
這種就是最常用的觀察者模式了,其實就是實現了多觀察者同時觀察同一個對象,人們美其名曰:發佈訂閱。因爲被觀察對象給出了向該對象中註冊新的觀察者和刪除已有觀察者的接口。

四、總結

設計模式中有很多模式都是通過面向接口編程的思想來對強關聯的對象之間解耦的,面向接口給我們的程序帶來了更好的擴展性,而這種方法運用到不同的場景語義中便形成了不同的模式,也就是說很多模式不是技術上的不同(都是面向接口而已),而是將面向接口的思想運用在了不同的場景應用中而已。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章