設計模式系列-觀察者模式

         天氣漸漸開始變暖和了,天天做辦公室寫代碼感覺身體越來越差了,該活動活動了,跟快樂技術沙龍嗨皮吧秦春林交流後決定天氣暖和了在羣裏發佈一次小的戶外活動,大家一起出去爬爬山或者運動運動,放鬆放鬆自己的身體和心情啊!嗨皮吧QQ羣:190784175

          一、案例場景

           沒錯,按慣例上面就是今天設計模式的場景,那麼我們來分析一下下面的場景吧:首先,我們此次戶外活動的組織者是快樂技術沙龍嗨皮吧(命名爲:HappyBar)。嗨皮吧負責維護所有參與的成員,並且通知成員活動的具體時間與集合的地點,並且成員需要觀察嗨皮吧的通知來獲得活動的消息。那麼我們來用代碼實現一把:

           1.快樂技術沙龍HappyBar的實現:

              ① 首先需要維護一個成員的集合,這個集合存放所有參加活動的成員,包括添加成員,如果有成員臨時有事就需要從集合中移除,所以還需要一個移除成員。

              ② 接下來有一個發佈通知的方法通知所有成員活動信息已經發布了。

              ③ 最後就是活動的詳細信息、代碼如下:

 
  1. /// <summary>  
  2.     /// 嗨皮吧 春遊計劃  
  3.     /// </summary>  
  4.     public class HappyBar  
  5.     {  
  6.         //維護QQ羣報名參與活動的成員  
  7.         List<QQMember> memberList = new List<QQMember>();  
  8.  
  9.         /// <summary>  
  10.         /// 增加參與活動的成員  
  11.         /// </summary>  
  12.         /// <param name="member">參與活動的成員</param>  
  13.         public void Attach(QQMember member)  
  14.         {  
  15.             memberList.Add(member);  
  16.         }  
  17.  
  18.         /// <summary>  
  19.         /// 移除活動的成員  
  20.         /// </summary>  
  21.         /// <param name="member"></param>  
  22.         public void Detach(QQMember member)  
  23.         {  
  24.             memberList.Remove(member);  
  25.         }  
  26.  
  27.         /// <summary>  
  28.         /// 發佈活動的通知  
  29.         /// </summary>  
  30.         public void Notify()  
  31.         {  
  32.             memberList.ForEach(p => p.GoDestination());  
  33.         }  
  34.  
  35.         /// <summary>  
  36.         /// 活動發佈的信息  
  37.         /// </summary>  
  38.         public string PublishInfo  
  39.         {  
  40.             get;  
  41.             set;  
  42.         }  
  43.     }  
 

           2.  活動參與者成員Member的實現 

          ①參與人有自己的基本信息例如名字。

          ②參與人要讀取活動的通知並且按時前往目的地,代碼如下:

  1. /// <summary>  
  2.     /// 嗨皮吧QQ羣成員類  
  3.     /// </summary>  
  4.     public class QQMember  
  5.     {  
  6.         //參與人姓名  
  7.         public string Name { getset; }  
  8.         //該參與人需要關注的通知來源  
  9.         public HappyBar happyBar { getset; }  
  10.  
  11.         /// <summary>  
  12.         /// 實際行動,根據活動信息前往目的地  
  13.         /// </summary>  
  14.         public void GoDestination()  
  15.         {  
  16.             Console.WriteLine("{0} 正在前往 {1}..",Name,happyBar.PublishInfo);  
  17.         }  
  18.     }  

           3.主函數調用代碼如下:
  1. static void Main(string[] args)  
  2.        {  
  3.            //春遊活動組織發起者  
  4.            HappyBar bar = new HappyBar();  
  5.  
  6.            QQMember memberZhang = new QQMember() { Name="張智", happyBar = bar };  
  7.            QQMember memberDing = new QQMember() { Name="丁朝陽", happyBar = bar };  
  8.            QQMember memberZheng = new QQMember() { Name = "鄭亞敏", happyBar = bar };  
  9.  
  10.            bar.Attach(memberZhang); //張智參加活動  
  11.            bar.Attach(memberDing);  //丁朝陽參加活動  
  12.            bar.Attach(memberZheng); //鄭亞敏參加互動  
  13.  
  14.            bar.Detach(memberZhang); //張智臨時有事,取消活動  
  15.  
  16.            //設置活動信息  
  17.            bar.PublishInfo = " 懷柔水長城 時間:2012年4月XX日";  
  18.  
  19.            //發起活動及通知  
  20.            bar.Notify();  
  21.  
                 運行結果如下:

          二、觀察者模式

           上邊的代碼顯然耦合度太高,如果這個時候我流程發起的組織不是嗨皮吧了呢? 是不是要重新加一個發起的類,並且修改成員關注通知的屬性。在如果發起的活動不是指面對QQ羣成員,還面對社區成員呢?是不是就需要加入一個社區成員的成員類,那麼活動發起類的集合就需要重構了。觀察者模式提供了一個很好的方案,即能擴展又能滿足我們現有的需求。

          1. 觀察者模式介紹

           觀察者模式類圖如下:

          

          ① Subject接口:抽象了主題類,這樣將來不管是什麼主題只要實現這個接口就可以發起主題活動。

          ② Observer接口:抽象了觀察者類,不管是什麼類型的觀察者都可以通過實現觀察者接口來具有觀察主題通知的功能。

          ③ ConcreteSubject類:實現了主題抽象,實現具體的功能。對應我們上邊的 HappyBar類,用來維護和發起活動通知。

          ④ ConreteObserver類:實現了觀察者的抽象,實現具體功能。對應我們上邊的QQMember類,用來觀察主題類的通知,並更新自身的狀態。

        2.重構我們的代碼

         首先我們需要抽象出主題類的規則與觀察者類,這樣我們將來不管是什麼主題和觀察者,都可以通過實現各自定義抽象來擴展程序,從而避免了修改程序。代碼如下:

  1. public interface ISubject  
  2.    {  
  3.        //添加成員  
  4.        void Attach(IObserver member);  
  5.        //移除成員  
  6.        void Detach(IObserver member);  
  7.        //通知  
  8.        void Notify();  
  9.        string PublishInfo { getset; }  
  10.    }  
  11.  
  12.    public interface IObserver  
  13.    {  
  14.        //根據通知更新自己  
  15.        void GoUpdate();  
  16.    }  
        接下來我們需要將我們之前的主題類也就是活動發起類HappyBar做一些修改讓自己實現ISubject接口,並修改成員集合的類型爲抽象的IObserver接口類型,代碼如下:
  1. /// <summary>  
  2.    /// 嗨皮吧 春遊計劃  
  3.    /// </summary>  
  4.    public class HappyBar : ISubject  
  5.    {  
  6.        //維護QQ羣報名參與活動的成員  
  7.        List<IObserver> memberList = new List<IObserver>();  
  8.  
  9.        /// <summary>  
  10.        /// 增加參與活動的成員  
  11.        /// </summary>  
  12.        /// <param name="member">參與活動的成員</param>  
  13.        public void Attach(IObserver member)  
  14.        {  
  15.            memberList.Add(member);  
  16.        }  
  17.  
  18.        /// <summary>  
  19.        /// 移除活動的成員  
  20.        /// </summary>  
  21.        /// <param name="member"></param>  
  22.        public void Detach(IObserver member)  
  23.        {  
  24.            memberList.Remove(member);  
  25.        }  
  26.  
  27.        /// <summary>  
  28.        /// 發佈活動的通知  
  29.        /// </summary>  
  30.        public void Notify()  
  31.        {  
  32.            memberList.ForEach(p => p.GoUpdate());  
  33.        }  
  34.  
  35.        /// <summary>  
  36.        /// 活動發佈的信息  
  37.        /// </summary>  
  38.        public string PublishInfo  
  39.        {  
  40.            get;  
  41.            set;  
  42.        }  
  43.    }  
        繼續重構成員類的實現,一樣實現成員類的接口即可。代碼如下:
  1. /// <summary>  
  2.     /// 嗨皮吧QQ羣成員類  
  3.     /// </summary>  
  4.     public class QQMember : IObserver  
  5.     {  
  6.         //參與人姓名  
  7.         public string Name { getset; }  
  8.         //該參與人需要關注的通知來源  
  9.         public HappyBar happyBar { getset; }  
  10.  
  11.         /// <summary>  
  12.         /// 實際行動,根據活動信息前往目的地  
  13.         /// </summary>  
  14.         public void GoUpdate()  
  15.         {  
  16.             Console.WriteLine("{0} 正在前往 {1}..",Name,happyBar.PublishInfo);  
  17.         }  
  18.     }  
         主函數調用如下:
            3.程序的擴展
  1. //春遊活動組織發起者  
  2.             HappyBar bar = new HappyBar();  
  3.  
  4.             IObserver memberZhang = new QQMember() { Name="張智", happyBar = bar };  
  5.             IObserver memberDing = new QQMember() { Name = "丁朝陽", happyBar = bar };  
  6.             IObserver memberZheng = new QQMember() { Name = "鄭亞敏", happyBar = bar };  
  7.  
  8.             bar.Attach(memberZhang); //張智參加活動  
  9.             bar.Attach(memberDing);  //丁朝陽參加活動  
  10.             bar.Attach(memberZheng); //鄭亞敏參加互動  
  11.  
  12.             bar.Detach(memberZhang); //張智臨時有事,取消活動  
  13.  
  14.             //設置活動信息  
  15.             bar.PublishInfo = " 懷柔水長城 時間:2012年4月XX日";  
  16.  
  17.             //發起活動及通知  
  18.             bar.Notify();  

          這時我們如果想要新增一個主題發起者不是嗨皮吧,而是參加Windows Phone7 與 Windows8開發者訓練營的主題,發起者是微軟。我們擴展的代碼如下:

  1. public class MicrosoftCenter : ISubject  
  2.     {  
  3.         List<IObserver> memberList = new List<IObserver>();  
  4.  
  5.         /// <summary>  
  6.         /// 增加參與活動的成員  
  7.         /// </summary>  
  8.         /// <param name="member">參與活動的成員</param>  
  9.         public void Attach(IObserver member)  
  10.         {  
  11.             memberList.Add(member);  
  12.         }  
  13.  
  14.         /// <summary>  
  15.         /// 移除活動的成員  
  16.         /// </summary>  
  17.         /// <param name="member"></param>  
  18.         public void Detach(IObserver member)  
  19.         {  
  20.             memberList.Remove(member);  
  21.         }  
  22.  
  23.         /// <summary>  
  24.         /// 發佈活動的通知  
  25.         /// </summary>  
  26.         public void Notify()  
  27.         {  
  28.             memberList.ForEach(p => p.GoUpdate());  
  29.         }  
  30.  
  31.         /// <summary>  
  32.         /// 活動發佈的信息  
  33.         /// </summary>  
  34.         public string PublishInfo  
  35.         {  
  36.             get;  
  37.             set;  
  38.         }  
  39.     }  
         當然可以參與的成員有,開發者、學生、老闆等等類型的觀察者,我們只要將不同類型的觀察者類型實現對應的觀察者抽象即可,代碼如下:
  1. /// <summary>  
  2.     /// 開發者  
  3.     /// </summary>  
  4.     public class Developer : IObserver  
  5.     {     
  6.         //參與人姓名  
  7.         public string Name { getset; }  
  8.         //該參與人需要關注的通知來源  
  9.         public ISubject subject { getset; }  
  10.  
  11.         /// <summary>  
  12.         /// 實際行動,根據活動信息前往目的地  
  13.         /// </summary>  
  14.         public void GoUpdate()  
  15.         {  
  16.             Console.WriteLine("{0} 正在前往 {1}..", Name, subject.PublishInfo);  
  17.         }  
  18.     }  
  19.  
  20.     /// <summary>  
  21.     /// 學生  
  22.     /// </summary>  
  23.     public class Student : IObserver  
  24.     {  
  25.         //參與人姓名  
  26.         public string Name { getset; }  
  27.         //該參與人需要關注的通知來源  
  28.         public ISubject subject { getset; }  
  29.  
  30.         /// <summary>  
  31.         /// 實際行動,根據活動信息前往目的地  
  32.         /// </summary>  
  33.         public void GoUpdate()  
  34.         {  
  35.             Console.WriteLine("{0} 正在前往 {1}..", Name, subject.PublishInfo);  
  36.         }  
  37.     }  
    

       主函數調用如下:

  1.  static void Main(string[] args)  
  2.         {  
  3.             //微軟開發者訓練營  
  4.             MicrosoftCenter mc = new MicrosoftCenter();  
  5.  
  6.             IObserver member1 = new Developer() { Name = "張三", happyBar = mc };  
  7.             IObserver member2 = new Developer() { Name = "李四", happyBar = mc };  
  8.  
  9.             mc.Attach(member1);  
  10.             mc.Attach(member2);  
  11.             mc.PublishInfo = "微軟望京開發者訓練營";  
  12.             mc.Notify();  
  13. }  
         運行結果如下:

    

          三、C#版觀察者模式

           理解了觀察者模式這種通知機制後,我們能感覺到,就是當發起通知時去調用通知所有成員。這類似於C#中的委託,如果該場景在C#中應用,使用委託可以更靈活方便,代碼不一定非要模式,簡單就是美!使用委託重構方法如下:

           1.聲明一個通知更新的委託:

  1. //定義委託  
  2.     delegate void EventHandler();  

           2.定義春遊活動主題:
  1. /// <summary>  
  2.     /// 嗨皮吧 春遊計劃  
  3.     /// </summary>  
  4.     public class HappyBar   
  5.     {  
  6.         //定義一個事件,當發佈活動的時候觸發  
  7.         public event EventHandler GoUpdate;  
  8.  
  9.         /// <summary>  
  10.         /// 發佈活動的通知  
  11.         /// </summary>  
  12.         public void Notify()  
  13.         {  
  14.             GoUpdate();  
  15.         }  
  16.  
  17.         /// <summary>  
  18.         /// 活動發佈的信息  
  19.         /// </summary>  
  20.         public string PublishInfo  
  21.         {  
  22.             get;  
  23.             set;  
  24.         }  
  25.     }  
          3.定義參與者(觀察者)成員
  1. /// <summary>  
  2.     /// 嗨皮吧QQ羣成員類  
  3.     /// </summary>  
  4.     public class QQMember   
  5.     {  
  6.         //參與人姓名  
  7.         public string Name { getset; }  
  8.         //該參與人需要關注的通知來源  
  9.         public HappyBar happyBar { getset; }  
  10.  
  11.         /// <summary>  
  12.         /// 實際行動,根據活動信息前往目的地  
  13.         /// </summary>  
  14.         public void GoUpdate()  
  15.         {  
  16.             Console.WriteLine("{0} 正在前往 {1}..", Name, happyBar.PublishInfo);  
  17.         }  
  18.     }  
            4.主函數調用如下:
  1. static void Main(string[] args)  
  2.        {  
  3.            HappyBar hb = new HappyBar();  
  4.  
  5.            QQMember member1 = new QQMember() { Name = "張三", happyBar = hb };  
  6.            QQMember member2 = new QQMember() { Name = "李四", happyBar = hb };  
  7.  
  8.            hb.PublishInfo = " 懷柔水長城 時間:2012年4月XX日";  
  9.            hb.GoUpdate += new 觀察者模式1.EventHandler(member1.GoToUpdate);  
  10.            hb.GoUpdate += new 觀察者模式1.EventHandler(member2.GoToUpdate);  
  11.  
  12.            hb.Notify();  
  13.  
            5.運行結果如下:

  

       觀察者模式結束啦,戶外活動具體時間還沒有確定,使用委託事件的代碼現在還是處於高耦合的狀態,如何解耦就看大家啦,可以把代碼以留言的形式跟大家分享!~ 呵呵,順便給個推薦吧!寫的很辛苦啊!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章