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


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

          一、案例場景

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

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

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

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

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

  ///   <summary>
    
///  嗨皮吧 春游計划
    
///   </summary>
     public  class HappyBar
    {
         // 維護QQ群報名參與活動的成員
        List<QQMember> memberList =  new List<QQMember>();

         ///   <summary>
        
///  增加參與活動的成員
        
///   </summary>
        
///   <param name="member"> 參與活動的成員 </param>
         public  void Attach(QQMember member)
        {
            memberList.Add(member);
        }

         ///   <summary>
        
///  移除活動的成員
        
///   </summary>
        
///   <param name="member"></param>
         public  void Detach(QQMember member)
        {
            memberList.Remove(member);
        }

         ///   <summary>
        
///  發布活動的通知
        
///   </summary>
         public  void Notify()
        {
            memberList.ForEach(p => p.GoDestination());
        }

         ///   <summary>
        
///  活動發布的信息
        
///   </summary>
         public  string PublishInfo
        {
             get;
             set;
        }
    }

 

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

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

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

 /// <summary>
    
/// 嗨皮吧QQ群成員類
    
/// </summary>
    public class QQMember
    {
        //參與人姓名
        public string Name { getset; }
        //該參與人需要關注的通知來源
        public HappyBar happyBar { getset; }

        /// <summary>
        
/// 實際行動,根據活動信息前往目的地
        
/// </summary>
        public void GoDestination()
        {
            Console.WriteLine("{0} 正在前往 {1}..",Name,happyBar.PublishInfo);
        }
    }

         3.主函數調用代碼如下:

static void Main(string[] args)
        {
            //春游活動組織發起者
            HappyBar bar = new HappyBar();

            QQMember memberZhang = new QQMember() { Name="張智", happyBar = bar };
            QQMember memberDing = new QQMember() { Name="丁朝陽", happyBar = bar };
            QQMember memberZheng = new QQMember() { Name = "鄭亞敏", happyBar = bar };

            bar.Attach(memberZhang); //張智參加活動
            bar.Attach(memberDing);  //丁朝陽參加活動
            bar.Attach(memberZheng); //鄭亞敏參加互動

            bar.Detach(memberZhang); //張智臨時有事,取消活動

            
//設置活動信息
            bar.PublishInfo = " 懷柔水長城 時間:2012年4月XX日";

            //發起活動及通知
            bar.Notify();
}

          運行結果如下:

          二、觀察者模式

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

          1. 觀察者模式介紹

           觀察者模式類圖如下:

          

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

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

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

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

        2.重構我們的代碼

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

  public  interface ISubject
    {
         // 添加成員
         void Attach(IObserver member);
         // 移除成員
         void Detach(IObserver member);
         // 通知
         void Notify();
        string PublishInfo { get; set; }
    }

     public  interface IObserver
    {
         // 根據通知更新自己
         void GoUpdate();
    }

        接下來我們需要將我們之前的主題類也就是活動發起類HappyBar做一些修改讓自己實現ISubject接口,並修改成員集合的類型為抽象的IObserver接口類型,代碼如下:

  ///   <summary>
    
///  嗨皮吧 春游計划
    
///   </summary>
     public  class HappyBar : ISubject
    {
         // 維護QQ群報名參與活動的成員
        List<IObserver> memberList =  new List<IObserver>();

         ///   <summary>
        
///  增加參與活動的成員
        
///   </summary>
        
///   <param name="member"> 參與活動的成員 </param>
         public  void Attach(IObserver member)
        {
            memberList.Add(member);
        }

         ///   <summary>
        
///  移除活動的成員
        
///   </summary>
        
///   <param name="member"></param>
         public  void Detach(IObserver member)
        {
            memberList.Remove(member);
        }

         ///   <summary>
        
///  發布活動的通知
        
///   </summary>
         public  void Notify()
        {
            memberList.ForEach(p => p.GoUpdate());
        }

         ///   <summary>
        
///  活動發布的信息
        
///   </summary>
         public  string PublishInfo
        {
             get;
             set;
        }
    }

        繼續重構成員類的實現,一樣實現成員類的接口即可。代碼如下:

  ///   <summary>
    
///  嗨皮吧QQ群成員類
    
///   </summary>
     public  class QQMember : IObserver
    {
         // 參與人姓名
         public  string Name {  getset; }
         // 該參與人需要關注的通知來源
         public HappyBar happyBar {  getset; }

         ///   <summary>
        
///  實際行動,根據活動信息前往目的地
        
///   </summary>
         public  void GoUpdate()
        {
            Console.WriteLine( " {0} 正在前往 {1}.. ",Name,happyBar.PublishInfo);
        }
    }

        主函數調用如下:

    // 春游活動組織發起者
            HappyBar bar =  new HappyBar();

            IObserver memberZhang =  new QQMember() { Name= " 張智 ", happyBar = bar };
            IObserver memberDing =  new QQMember() { Name =  " 丁朝陽 ", happyBar = bar };
            IObserver memberZheng =  new QQMember() { Name =  " 鄭亞敏 ", happyBar = bar };

            bar.Attach(memberZhang);  // 張智參加活動
            bar.Attach(memberDing);   // 丁朝陽參加活動
            bar.Attach(memberZheng);  // 鄭亞敏參加互動

            bar.Detach(memberZhang);  // 張智臨時有事,取消活動

            
// 設置活動信息
            bar.PublishInfo =  "  懷柔水長城 時間:2012年4月XX日 ";

             // 發起活動及通知
            bar.Notify();

         3.程序的擴展

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

  public  class MicrosoftCenter : ISubject
    {
        List<IObserver> memberList =  new List<IObserver>();

         ///   <summary>
        
///  增加參與活動的成員
        
///   </summary>
        
///   <param name="member"> 參與活動的成員 </param>
         public  void Attach(IObserver member)
        {
            memberList.Add(member);
        }

         ///   <summary>
        
///  移除活動的成員
        
///   </summary>
        
///   <param name="member"></param>
         public  void Detach(IObserver member)
        {
            memberList.Remove(member);
        }

         ///   <summary>
        
///  發布活動的通知
        
///   </summary>
         public  void Notify()
        {
            memberList.ForEach(p => p.GoUpdate());
        }

         ///   <summary>
        
///  活動發布的信息
        
///   </summary>
         public  string PublishInfo
        {
             get;
             set;
        }
    }

        當然可以參與的成員有,開發者、學生、老板等等類型的觀察者,我們只要將不同類型的觀察者類型實現對應的觀察者抽象即可,代碼如下:

    ///   <summary>
    
///  開發者
    
///   </summary>
     public  class Developer : IObserver
    {   
         // 參與人姓名
         public  string Name {  getset; }
         // 該參與人需要關注的通知來源
         public ISubject subject {  getset; }

         ///   <summary>
        
///  實際行動,根據活動信息前往目的地
        
///   </summary>
         public  void GoUpdate()
        {
            Console.WriteLine( " {0} 正在前往 {1}.. ", Name, subject.PublishInfo);
        }
    }

     ///   <summary>
    
///  學生
    
///   </summary>
     public  class Student : IObserver
    {
         // 參與人姓名
         public  string Name {  getset; }
         // 該參與人需要關注的通知來源
         public ISubject subject {  getset; }

         ///   <summary>
        
///  實際行動,根據活動信息前往目的地
        
///   </summary>
         public  void GoUpdate()
        {
            Console.WriteLine( " {0} 正在前往 {1}.. ", Name, subject.PublishInfo);
        }
    }

 

       主函數調用如下:

    static  void Main( string[] args)
        {
             // 微軟開發者訓練營
            MicrosoftCenter mc =  new MicrosoftCenter();

            IObserver member1 =  new Developer() { Name =  " 張三 ", happyBar = mc };
            IObserver member2 =  new Developer() { Name =  " 李四 ", happyBar = mc };

            mc.Attach(member1);
            mc.Attach(member2);
            mc.PublishInfo =  " 微軟望京開發者訓練營 ";
            mc.Notify();
}

       運行結果如下:

    

          三、C#版觀察者模式

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

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

 //定義委托
    delegate void EventHandler();

          2.定義春游活動主題:

 /// <summary>
    
/// 嗨皮吧 春游計划
    
/// </summary>
    public class HappyBar 
    {
        //定義一個事件,當發布活動的時候觸發
        public event EventHandler GoUpdate;

        /// <summary>
        
/// 發布活動的通知
        
/// </summary>
        public void Notify()
        {
            GoUpdate();
        }

        /// <summary>
        
/// 活動發布的信息
        
/// </summary>
        public string PublishInfo
        {
            get;
            set;
        }
    }

         3.定義參與者(觀察者)成員

/// <summary>
    
/// 嗨皮吧QQ群成員類
    
/// </summary>
    public class QQMember 
    {
        //參與人姓名
        public string Name { getset; }
        //該參與人需要關注的通知來源
        public HappyBar happyBar { getset; }

        /// <summary>
        
/// 實際行動,根據活動信息前往目的地
        
/// </summary>
        public void GoUpdate()
        {
            Console.WriteLine("{0} 正在前往 {1}..", Name, happyBar.PublishInfo);
        }
    }

            4.主函數調用如下:

  static void Main(string[] args)
        {
            HappyBar hb = new HappyBar();

            QQMember member1 = new QQMember() { Name = "張三", happyBar = hb };
            QQMember member2 = new QQMember() { Name = "李四", happyBar = hb };
            hb.PublishInfo = " 懷柔水長城 時間:2012年4月XX日";
            hb.GoUpdate += new 觀察者模式1.EventHandler(member1.GoToUpdate);
            hb.GoUpdate += new 觀察者模式1.EventHandler(member2.GoToUpdate);

            hb.Notify();
}

           5.運行結果如下:

  

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM