閱讀795 返回首頁    go 阿裏雲 go 技術社區[雲棲]


淺學設計模式之觀察者<Observer>模式及在android中的應用

轉自https://www.eoeandroid.com/home.php?mod=space&uid=871316&do=blog&id=48239


最近在學習下設計模式,而加深學習的不錯的方法就是把心得寫出來吧。記錄下自己的理解。現在自己看的書是《head.Frist設計模式》這本書。比較不錯,想學習設計模式的朋友可以看下這本書。


        觀察者<Observer>模式(有時又被稱為發布-訂閱<Publish/Subscribe>模式、模型-視圖<Model/View>模式、源-收聽者<Source/Listener>模式或從屬者<Dependents>模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過唿叫各觀察者所提供的方法來實現。此種模式通常被用來實作事件處理係統。(源自百度百科)

           (五月三十一號修正內容)

        看下結構圖:


     開始一個小例子

Observer接口:

     

  1. public interface ISubscribe {  
  2.     void getNewPaper();  
  3. }  

實現Observer接口的觀察者:

個人訂閱者:

  1. public class PersonalSubscriber implements ISubscribe {  
  2.     private String strName;  
  3.       
  4.     public void setNewsPaperName(String strName){  
  5.         this.strName = strName;  
  6.     }  
  7.       
  8.     public String getNewsPaperName(){  
  9.         return strName;  
  10.     }  
  11.   
  12.     @Override  
  13.     public void getNewPaper() {  
  14.         // TODO Auto-generated method stub  
  15.         System.out.println("我是個人用戶,我得到了我的報紙:"+getNewsPaperName());  
  16.     }  
  17.   
  18. }  

企業訂閱者:

  1. public class EnterpriseSubscriber implements ISubscribe {  
  2.     private String strName;  
  3.       
  4.     public void setNewsPaperName(String strName){  
  5.         this.strName = strName;  
  6.     }  
  7.       
  8.     public String getNewsPaperName(){  
  9.         return strName;  
  10.     }  
  11.     @Override  
  12.     public void getNewPaper() {  
  13.         // TODO Auto-generated method stub  
  14.         System.out.println("我是企業用戶,我得到了我的報紙:"+getNewsPaperName());  
  15.     }  
  16.   
  17. }  

被觀察者:Subject

  1. public abstract class Publish {   
  2.     public List<ISubscribe> list;  
  3.     public Publish(){  
  4.         list = new ArrayList();  
  5.     }  
  6.       
  7.     public void registered(ISubscribe iSubscribe){  
  8.         list.add(iSubscribe);  
  9.     }  
  10.       
  11.     public void unregistered(ISubscribe iSubscribe){  
  12.         list.remove(iSubscribe);  
  13.     }  
  14.       
  15.     public abstract void sendNewsPaper();  
  16. }  

這裏使用的是抽象類

下麵是實現:

  1. public class PostOffice extends Publish{  
  2.   
  3.     @Override  
  4.     public void sendNewsPaper() {  
  5.         // TODO Auto-generated method stub  
  6.         Iterator iterator = list.iterator();  
  7.         while(iterator.hasNext()){  
  8.             ((ISubscribe) iterator.next()).getNewPaper();  
  9.         }  
  10.     }  
  11.       
  12. }  

測試:

  1. public static void main(String[] args) {  
  2.         // TODO Auto-generated method stub  
  3.         PostOffice postOffice = new PostOffice();  
  4.           
  5.         //得到個人用戶  
  6.         PersonalSubscriber person = new PersonalSubscriber();  
  7.         person.setNewsPaperName("《南方周末》");  
  8.         //得到企業用戶  
  9.         EnterpriseSubscriber enterprise = new EnterpriseSubscriber();  
  10.         enterprise.setNewsPaperName("《企業報》");  
  11.           
  12.         //注冊觀察者  
  13.         postOffice.registered(person);  
  14.         postOffice.registered(enterprise);  
  15.           
  16.         //發放報紙  
  17.         postOffice.sendNewsPaper();  
  18.     }  

測試結果:

  1. 我是個人用戶,我得到了我的報紙:《南方周末》  
  2. 我是企業用戶,我得到了我的報紙:《企業報》  



下麵是有些錯誤五月18號版本:後麵有錯誤解析!

        舉個例子,張三從郵局訂閱了《南方周末》,李四從郵局訂閱了《新京報》,王五從郵局裏麵訂閱了《南方都市報》。當報紙抵達郵局的時候,郵局就會把報紙送遞訂閱者。而不需要訂閱者天天到郵局詢問報紙是否到達郵局。

       下麵開始代碼:首先應該是個訂閱者接口:

  1. package cn.demo;  
  2.   
  3. public interface ISubscribe {  
  4.     //從郵局訂閱報紙  
  5.     public void registered(PostOffice postOffice);  
  6.     //從郵局退訂報紙  
  7.     public void unregistered(PostOffice postOffice);  
  8.     //獲取報紙名稱  
  9.     public void getNewsPaper();  
  10. }  

第三個方法非必須,前兩個方法必須要有。(應修改為前兩個非必須,第三個方法必須有)

然後有三個訂閱者類:

張三:

  1. package cn.demo;  
  2.   
  3. public class ZhangSan implements ISubscribe{  
  4.     private String mName;  
  5.     private String mNewsPaperName;  
  6.       
  7.     public ZhangSan(String mName, String mNewsPaperName) {  
  8.         this.mName = mName;  
  9.         this.mNewsPaperName = mNewsPaperName;  
  10.     }  
  11.       
  12.     public String getName() {  
  13.         return mName;  
  14.     }  
  15.   
  16.     public String getNewsPaperName() {  
  17.         return mNewsPaperName;  
  18.     }  
  19.       
  20.     @Override  
  21.     public void registered(PostOffice postOffice){  
  22.         postOffice.registeredNewsPaper(this);  
  23.     }  
  24.       
  25.     @Override  
  26.     public void unregistered(PostOffice postOffice){  
  27.         postOffice.unregisteredNewsPaper(this);  
  28.     }  
  29.   
  30.     @Override  
  31.     public void getNewsPaper() {  
  32.         // TODO Auto-generated method stub  
  33.         System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());  
  34.     }  
  35.   
  36. }  

李四:

  1. package cn.demo;  
  2.   
  3. public class Lisi implements ISubscribe{  
  4.     private String mName;  
  5.     private String mNewsPaperName;  
  6.       
  7.     public Lisi(String mName, String mNewsPaperName) {  
  8.         this.mName = mName;  
  9.         this.mNewsPaperName = mNewsPaperName;  
  10.     }  
  11.       
  12.     public String getName() {  
  13.         return mName;  
  14.     }  
  15.   
  16.     public String getNewsPaperName() {  
  17.         return mNewsPaperName;  
  18.     }  
  19.       
  20.     @Override  
  21.     public void unregistered(PostOffice postOffice){  
  22.         postOffice.unregisteredNewsPaper(this);  
  23.     }  
  24.       
  25.     @Override  
  26.     public void registered(PostOffice postOffice){  
  27.         postOffice.registeredNewsPaper(this);  
  28.     }  
  29.   
  30.     @Override  
  31.     public void getNewsPaper() {  
  32.         // TODO Auto-generated method stub  
  33.         System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());  
  34.     }  
  35.   
  36. }  

王五:

  1. package cn.demo;  
  2.   
  3. public class Wangwu implements ISubscribe{  
  4.     private String mName;  
  5.     private String mNewsPaperName;  
  6.       
  7.     public Wangwu(String mName, String mNewsPaperName) {  
  8.         this.mName = mName;  
  9.         this.mNewsPaperName = mNewsPaperName;  
  10.     }  
  11.       
  12.     public String getName() {  
  13.         return mName;  
  14.     }  
  15.   
  16.     public String getNewsPaperName() {  
  17.         return mNewsPaperName;  
  18.     }  
  19.       
  20.     @Override  
  21.     public void registered(PostOffice postOffice){  
  22.         postOffice.registeredNewsPaper(this);  
  23.     }  
  24.       
  25.     @Override  
  26.     public void unregistered(PostOffice postOffice){  
  27.         postOffice.unregisteredNewsPaper(this);  
  28.     }  
  29.   
  30.   
  31.     @Override  
  32.     public void getNewsPaper() {  
  33.         // TODO Auto-generated method stub  
  34.         System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());  
  35.     }  
  36.   
  37. }  

三個訂閱者,都有方法訂閱報紙,或者取消訂閱。其實關於訂閱者的信息,是存儲在郵局類裏麵。


郵局類:(郵局類應該繼承一個抽象類或者接口(Subject),這裏沒有實現)

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. public class PostOffice {  
  5.     private List<ISubscribe> SubscribeList = new ArrayList<ISubscribe>();  
  6.       
  7.     public void registeredNewsPaper(ISubscribe subscribe) {  
  8.         SubscribeList.add(subscribe);  
  9.     }  
  10.       
  11.     public void unregisteredNewsPaper(ISubscribe subscribe) {  
  12.         if (subscribe != null) {  
  13.             SubscribeList.remove(subscribe);  
  14.         }  
  15.           
  16.     }  
  17.       
  18.     public void getNewsPaper(boolean bool) {  
  19.         if (bool) {  
  20.             sendNewsPaper();  
  21.         }  
  22.     }  
  23.       
  24.     public void sendNewsPaper() {  
  25.         for(ISubscribe subscribe : SubscribeList) {  
  26.             subscribe.getNewsPaper();  
  27.         }  
  28.     }  
  29. }  
在這個類裏麵,有變化通知是使用的sendNewsPaper()這個方法,遍曆所有的訂閱者。
測試類:

  1. public static void main(String[] args) {  
  2.         // TODO Auto-generated method stub  
  3.         PostOffice mPostOffice = new PostOffice();  
  4.         ISubscribe zhangsan = new ZhangSan("張三""《南方周末》");  
  5.         ISubscribe lisi = new Lisi("李四""《新京報》");  
  6.         ISubscribe wangwu = new Wangwu("王五""《南方都市報》");  
  7.           
  8.         //開始訂閱報紙  
  9.         zhangsan.registered(mPostOffice);  
  10.         lisi.registered(mPostOffice);  
  11.         wangwu.registered(mPostOffice);  
  12.           
  13.         //郵局收到報紙,開始發放  
  14.         mPostOffice.getNewsPaper(true);  
  15.           
  16.         //李四退訂  
  17.         lisi.unregistered(mPostOffice);  
  18.         //郵局收到報紙,開始發放  
  19.         mPostOffice.getNewsPaper(true);  
  20.     }  

打印結果:

    

  1. 我是張三,我收到我訂閱的《南方周末》  
  2. 我是李四,我收到我訂閱的《新京報》  
  3. 我是王五,我收到我訂閱的《南方都市報》  
  4. 我是張三,我收到我訂閱的《南方周末》  
  5. 我是王五,我收到我訂閱的《南方都市報》  

這裏隻是提供了一個簡單的例子,而且代碼你會發現有冗餘,三個訂閱者類,幾乎是一樣,因為沒有在類裏麵添加他們獨自的屬性,可以用一個Person類來替代。這裏寫這三個類是為了顯示清晰。

        關於五月18號的例子,觀察者沒必要有注冊和取消注冊的方法。他們的方法的實現也是調用的被觀察者的注冊和取消注冊,不如直接使用被觀察者的方法。

         在Android中,button.setOnclickListener()這個方式是比較常見的觀察者模式:當然,眾所周知,onClick是著名的回調方法,在這裏不會研究回調,不用太在意。

看下代碼:

  1. Button button1 = (Button)findViewById(R.id.button1);  
  2.         Button button2 = (Button)findViewById(R.id.button2);  
  3.         Button button3 = (Button)findViewById(R.id.button3);  
  4.         button1.setOnClickListener(this);  
  5.         button2.setOnClickListener(this);  
  6.         button3.setOnClickListener(this);  
  7.     }  
  8.   
  9.     @Override  
  10.     public void onClick(View v) {  
  11.         // TODO Auto-generated method stub  
  12.         switch(v.getId()) {  
  13.         case R.id.button1 :  
  14. //          do some thing         
  15.         case R.id.button2 :  
  16. //          do some thing  
  17.         case R.id.button3 :  
  18. //          do some thing  
  19.         }  
  20.     }  

先button.setOnclickListener()進行注冊。相當於郵局中的lisi.registered(mPostOffice),此處錯誤,應該是第一例中的postOffice.registered(person)。onclick響應事件相當於郵局中的

  1. public void sendNewsPaper() {  
  2.         for(ISubscribe subscribe : SubscribeList) {  
  3.             subscribe.getNewsPaper();  
  4.         }  
  5.     }  

這個中的subscribe.getNewsPaper()這個方法。View.onClickListener相當於郵局。View也就是button是我們的訂閱者,也就相當於張三。我們也可以在getNewsPaper中加一些自己的switch判斷。

       最後,如果我們的郵局要實現類似button這樣的回調這麼實現呢?

  1. ISubscribe isubscribe;  
  2.   
  3.     @Override  
  4.     public void getNewsPaper() {  
  5.         // TODO Auto-generated method stub  
  6.         System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());  
  7.         isubscribe.getNewsPaper();  
  8.     } 

最後更新:2017-04-03 08:26:14

  上一篇:go Android下修改SeekBar樣式
  下一篇:go ElasticSearch遠程任意代碼執行漏洞(CVE-2014-3120)分析