<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      觀察者模式(Observer)

      1.1.1 摘要

              在系統的設計中,我們常常需要設計一個消息提示功能,讓系統把提示信息發送到客戶端。做到這一點的設計方案可以是多種多樣,但是為了使系統能夠易于復用,我們的設計應該遵守低耦合高內聚的設計原則,而且減少對象之間的耦合有利于系統的復用。觀察者模式(Observer)是滿足這一要求的各種設計方案中最重要的一種。

             使用頻率 clip_image001

             觀察者模式(Observer):定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。

       

      1.1.2 正文

         observer0

      圖1觀察者(Observer)模式結構圖

       

              抽象主題(Subject)角色:主題角色把所有對觀察考對象的引用保存在一個聚集里,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,主題角色又叫做抽象被觀察者(Observable)角色,一般使用一個抽象類或者一個接口實現。

             抽象觀察者(Observer)角色:為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己。這個接口叫做更新接口。抽象觀察者角色一般用一個抽象類或者一個接口實現。在這個示意性的實現中,更新接口只包含一個方法(即Update()方法),這個方法叫做更新方法。

             具體主題(ConcreteSubject)角色:將有關狀態存入具體現察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者角色(Concrete Observable)。具體主題角色通常用一個具體子類實現。

             具體觀察者(ConcreteObserver)角色:存儲與主題的狀態自恰的狀態。具體現察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。如果需要,具體現察者角色可以保存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。

       

      觀察者模式與推拉模式

             推模式(Push):是一種基于客戶器/服務器機制,由服務器主動將信息發送到客戶器的技術。

             拉模式(Pull)與推模式(Push)恰好相反,是由客戶器主動向服務器發送請求的技術。

             接下來讓我們通過一段示例代碼介紹觀察者模式(Obsever),首先假設服務器狀態更新后要同時通知客戶端A,B,C直接的實現如下:

       

      /// <summary>
      /// The Server class and when it updated, should notify the clients.
      /// </summary>
      public class Server
      {
          /// <summary>
          /// Keeps three objects referen.
          /// </summary>
          /// <param name="a">The object of ClentA.</param>
          /// <param name="b">The object of ClentB.</param>
          /// <param name="c">The object of ClentC.</param>
          public Server(ClientA a, ClientB b, ClientC c)
          {
              ClientA = a;
              ClientB = b;
              ClientC = c;
          }
      
          public ClientA ClientA { get; set; }
          public ClientB ClientB { get; set; }
          public ClientC ClientC { get; set; }
          public string SysMsg { get; set; }
      }

           上面我們定義了服務器類,它包含了一個構造函數,通過它來保持ClientA,ClientB和ClientC類型對象的引用。

       

      /// <summary>
      /// The client client, it would receive message
      /// when the sever updated.
      /// </summary>
      public class ClientA
      {
          /// <summary>
          /// Receives the message from sever.
          /// </summary>
          /// <param name="sysMsg"></param>
          public void Notify(string sysMsg)
          {
              SysMsg = sysMsg;
          }
      
          public string SysMsg { get; set; }
      }
      
      public class ClientB
      {
          public void Notify(string sysMsg)
          {
              SysMsg = sysMsg;
          }
      
          public string SysMsg { get; set; }
      }
      
      public class ClientC
      {
          public void Notify(string sysMsg)
          {
              SysMsg = sysMsg;
          }
      
          public string SysMsg { get; set; }
      }

           接著我們定義了客戶端類ClientA,ClientB和ClientC,由于它們是被動地接受信息(推模式),所有它們只包含接受信息的方法Notify()。

         oberver3

      圖2示例類圖

       

           上圖我們知道Server類保持了ClientA,ClientB和ClientC類對象的引用。

       

      static class Program
      {
          /// <summary>
          /// 應用程序的主入口點。
          /// </summary>
          [STAThread]
          static void Main()
          {
              var server = new Server(
                  new ClientA(), new ClientB(), new ClientC()) { SysMsg = "Hi everyone" };
              server.ClientA.Notify(server.SysMsg);
              server.ClientB.Notify(server.SysMsg);
              server.ClientC.Notify(server.SysMsg);
          }
      }

             這里類型Server保持了ClientA,ClientB和ClientC類型的引用,但仔細考量一下如果我們要添加新的客戶端類ClinetD,那么我們就需要修改Server的實現,這也意味著Server過度地依賴于客戶端類,這嚴重地違反了面向對象設計的原則:面向接口編程,而非面向實現編程。其實ClientA,ClinetB和ClientC三個類型都有類似的功能——Notify()方法,所以我們可以為它們抽象出一個更高層的接口,通過封裝變化使得我們設計依賴于抽象而非具體。

       

      oberver4

      圖3面向接口而非實現示例類圖

       

             現在Server類只依賴于接口IClent,如果我們需要添加新的客戶端類,只需實現IClient接口就行了,而且Server不用做出任何修改。

             現在我們對于為什么要使用觀察者模式(Observer)有了初步的了解,接下來讓我們通過更詳細的例子介紹。

             現在我們來實現觀察者模式(Observer),假設Gof電信公司要把他們最新發明的設計模式介紹給每一位Geek,他們想通過服務器把信息發送到每位Geek的移動客戶端,我們很榮幸協助他們完成這一項目,OK首先我們先創建抽象接口類:IObserver,然后我們創建具體觀察者:MobilePhone,Tablet和Laptop,接著創建GofTelcom作為Subject。

             首先我們定義接口IObserver,它只含一個Update()方法,由于提供了統一接口使得我們設計面對接口編程,而非面向實現編程。

       

      /// <summary>
      /// The interface of observer.
      /// </summary>
      public interface IObserver
      {
          void Update(string msg);
      }

           接著定義具體的觀察者Mobile,Tablet和Laptop,它們都提供了Update()方法的具體實現。

       

      /// <summary>
      /// Concrete observer.
      /// </summary>
      public class MobilePhone : IObserver
      {
      
          #region IObserver 成員
      
          public void Update(string msg)
          {
              Console.WriteLine(String.Format("I am MobilePhone.\n New message: {0}", msg));
          }
      
          #endregion
      
      }
      
      
      /// <summary>
      /// Concrete observer.
      /// </summary>
      public class Tablet : IObserver
      {
          #region IObserver 成員
      
          public void Update(string msg)
          {
              Console.WriteLine(String.Format("OH...I am Tablet.\n New message: {0}", msg));
          }
      
          #endregion
      }
      
      /// <summary>
      /// Concrete observer.
      /// </summary>
      public class Laptop : IObserver
      {
          #region Implementation of IObserver
      
          public void Update(string msg)
          {
              Console.WriteLine(String.Format("OH...I am Laptop.\n New message: {0}", msg));
          }
      
          #endregion
      }

             現在我們定義被觀察者類GofTelecom,它定義了一個Notify()方法,委托GofNews()和事件GofNews。如果大家想參考什么是委托和事件可以參考《.NET 中的委托》《.NET中的委托和事件(續)》

       

      /// <summary>
      /// The Subject class.
      /// </summary>
      public class GofTelecom
      {
          public delegate void GofNews(string msg);
      
          public static event GofNews NewsEvent;
      
          /// <summary>
          /// Notifies every obervers.
          /// </summary>
          /// <returns>If notification is successful return true, otherwise false.</returns>
          public static bool Notify()
          {
              if (NewsEvent != null)
              {
                  NewsEvent(Message);
                  return false;
              }
              return true;
          }
      
          public static String Message { get; set; }
      }

            最后通過給GofTelecom對象添加MobilePhone,Tablet和Laptop對象來實現觀察者和被觀察者之間的關聯。

       

      static void Main(string[] args)
      {
          IList<IObserver> objObserver = new List<IObserver>();
          // Registers observer.
          objObserver.Add(new MobilePhone());
          objObserver.Add(new Tablet());
          objObserver.Add(new Laptop());
      
          // Attachs event and method.
          foreach (IObserver ob in objObserver)
          {
              GofTelecom.NewsEvent += ob.Update;
          }
      
          GofTelecom.Message = "Hi Everyone,/n We recommand you new Design Pattern.";
          Console.WriteLine(!GofTelecom.Notify() ? "Notify successful.\n" : "Notify failed.\n");
          Console.ReadKey();
      }

       

      observer4

      圖4示例輸出結果

       

             在系統設計過程中,我們的系統設計基本上都要提供消息提示功能類似于QQ或MSN的消息提示框,而且這個功能恰好是觀察者模式(Observer)實現的一種具體實現,在我們實現觀察者模式(Observer)消息提示功能之前,先讓我們回顧一下推拉模式吧!

      推模式和拉模式的區別:

             也許大家會好奇地問“使用推模式和拉模式,有什么區別呢?”

      推模式的好處是能夠及時響應,想要提供給Observer端什么數據,就將這些數據封裝成對象,傳遞給Observer,缺點是需要創建自定義的事件關聯信息,而且它必須繼承于EventArgs對象。

             缺點:精確性較差,不能保證能把信息送到客戶器。

             拉模式的好處則是不需要另外定義對象,直接將自身的引用傳遞進去就可以了。

             缺點:不能夠及時獲取系統的變更。

            .Net 中沒有內置的IObserver和IObservable接口(Java中提供該接口類),我們可以通過委托和事件來完成,但是一樣面臨選擇推模式還是拉模式的問題,何時使用哪種策略完全依賴于設計者,你也可以將兩種方式都實現了,接下來我們的消息提示功能將通過拉模式實現。

            首先我們通過給窗體控件添加屬性TimerStatus,客戶端通過Timer來發送消息請求到服務器端,TimerStatus是通過控件公開Timer屬性的設置,具體實現如下:

       

      /// <summary>
      /// The timer status, 
      /// it includes base properties to set up timer.
      /// </summary>
      [TypeConverterAttribute(typeof(ClockStatusTypeConverter))]
      public class TimerStatus
      {
          private Timer _timer = new Timer();
          private Frequencies _frequencies = Frequencies.Second;
          private int _interval = 23;
          private bool _start;
      
          /// <summary>
          /// Paramenterless constructor.
          /// </summary>
          public TimerStatus()
              : this(Frequencies.Minute, 23, true)
          {
      
          }
      
          public TimerStatus(Frequencies frequencies, int interval, bool start)
          {
              Frequencies = frequencies;
              Interval = interval;
              Start = start;
          }
      
          /// <summary>
          /// Gets or sets the frequencies.
          /// </summary>
          /// <value>
          /// The frequencies.
          /// </value>
          public Frequencies Frequencies
          {
              get { return _frequencies; }
              set { _frequencies = value; }
          }
      
          /// <summary>
          /// Gets or sets the interval.
          /// </summary>
          /// <value>
          /// The interval.
          /// </value>
          public int Interval
          {
              get { return _interval; }
              set { _interval = value; }
          }
      
          /// <summary>
          /// Gets or sets a value indicating 
          /// whether this <see cref="TimerStatus"/> is start.
          /// </summary>
          /// <value>
          ///   <c>true</c> if start; otherwise, <c>false</c>.
          /// </value>
          public bool Start
          {
              get { return _start; }
              set { _start = value; }
          }
      
          public Timer Timer
          {
              get
              {
                  if (null == _timer)
                      new Timer();
                  return _timer;
              }
              set { _timer = value; }
          }
      
      }

       

           定義一個頻率枚舉,它提供Second,Minute和Hour單位來設置Timer的頻率。

       

      /// <summary>
      /// The frequency of timer.
      /// </summary>
      public enum Frequencies
      {
          Second,
          Minute,
          Hour
      }

             現在我們完成了控件屬性TimerStatus的定義,接著把該屬性添加到我們自定義Form控件中,我們只需在自定義Form中添加屬性如下:

       

      /// <summary>
      /// Gets or sets the timer status.
      /// </summary>
      /// <value>
      /// The timer status.
      /// </value>
      [Browsable(true)]
      public TimerStatus TimerStatus
      {
          get
          {
              if (null == _timerStatus)
                  return new TimerStatus();
              return _timerStatus;
          }
          set { _timerStatus = value; }
      }

       

            但我們發現自定義控件屬性并不能編輯,這是由于控件沒有辦法把我們自定義類型TimeStatus轉換為控件中實現的類型(如:字符串)。所以我們需要添加轉換器類。

       

      observer7

      圖5消息提示控件屬性定義

       

       

      /// <summary>
      /// Defines <see cref="TimerStatus"/> type converter.
      /// </summary>
      public class TimerStatusTypeConverter : ExpandableObjectConverter
      {
          /// <summary>
          /// Checks whether can convert from a string to <see cref="TimerStatus"/> or not.
          /// </summary>
          /// <param name="context"></param>
          /// <param name="sourceType">Source Type.</param>
          /// <returns>Can return true, otherwise false.</returns>
          public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
          {
              // Can convert from String to ClockStatus.
              if (sourceType == typeof(String))
                  return true;
              return base.CanConvertFrom(context, sourceType);
          }
      
          /// <summary>
          /// Checks whether can convert from <see cref="TimerStatus"/> to string or not.
          /// </summary>
          /// <param name="context"></param>
          /// <param name="destinationType">Destination Type</param>
          /// <returns>Can return true, otherwise false.</returns>
          public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
          {
              if (destinationType == typeof(String))
                  return true;
              return base.CanConvertTo(context, destinationType);
          }
      
      
          /// <summary>
          /// Converts from stirng to <see cref="TimerStatus"/>.
          /// </summary>
          public override object ConvertFrom(ITypeDescriptorContext context,
              System.Globalization.CultureInfo culture, object value)
          {
              var convertValue = value as string;
      
              if (String.IsNullOrEmpty(convertValue))
                  return new TimerStatus();
      
              // Defines a enum convert.
              var enumConvert = TypeDescriptor.GetConverter(typeof(Frequencies));
      
              // Get the string corresponding to control properties.
              var values = convertValue.Split(',');
      
              if (values.Length < 3)
                  throw new ArgumentException("Wrong number of arguments");
      
              int result1 = -1;
              bool result2;
              if (int.TryParse(values[1].ToString(), out result1)
                  && bool.TryParse(values[2].ToString(), out result2))
                  return new TimerStatus(
                      (Frequencies)enumConvert.ConvertFromString(values[0]), result1, result2);
              return base.ConvertFrom(context, culture, value);
          }
      
          /// <summary>
          /// Converts from <see cref="TimerStatus"/> stirng.
          /// </summary>
          public override object ConvertTo(ITypeDescriptorContext context,
              System.Globalization.CultureInfo culture, object value, Type destinationType)
          {
              if (value is TimerStatus)
              {
                  if (destinationType == typeof(String))
                  {
                      var clockStatus = value as TimerStatus;
                      return String.Format("{0},{1},{2}",
                                           clockStatus.Frequencies, clockStatus.Interval, clockStatus.Start);
                  }
              }
              return base.ConvertTo(context, culture, value, destinationType);
          }
      
      }

            上面我們定義了自定義類型轉換器,它提供把TimerStatus類型轉換為字符串的方法和通過屬性參數創建TimerStatus對象的方法。

       

      observer6

      圖6消息提示控件屬性定義

       

             現在我們定義好了消息提示框,而且我們可以通過屬性瀏覽器直接編輯TimerStatus,如Frequencies設置Timer的頻率,Interval時間間隔和Start是否啟用Timer。

             每個客戶端通過拉模式訪問服務器端,當有信息就返回到服務器端顯示出來如下:

       

      observer5

      圖7消息提示框設計

       

      1.1.3 總結

             觀察者模式(Observer)的優點是實現了抽象與具體的分離,并定義了穩定的更新消息傳遞機制,類別清晰,并抽象了更新接口。

             但是其缺點是每個觀察者對象必須繼承這個抽像出來的接口類,這樣就造成了一些不方便,比如有一個別人寫的觀察者對象,并沒有繼承該抽象類,或者接口不對,我們又希望不修改該類直接使用它。雖然可以再應用Adapter模式來一定程度上解決這個問題,但是會造成更加復雜煩瑣的設計,增加出錯幾率。

      觀察者模式(Observer)的效果有以下幾個優點:

      • 觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體現察者聚集,每一個具體現察者都符合一個抽象觀察者的接口。被觀察者并不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
      • 觀察者模式支持廣播通信。被觀察者會向所有的登記過的觀察者發出通知。

       

      觀察者模式(Observer)有下面的一些缺點:

      • 如果一個被觀察者對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
      • 如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察考模式時要特別注意這一點。
      • 如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。
      • 雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎么發生變化的

       

      參考

      http://www.codeproject.com/KB/miscctrl/RobMisNotifyWindow.aspx

      posted @ 2011-11-30 22:42  JK_Rush  閱讀(9325)  評論(3)    收藏  舉報
      主站蜘蛛池模板: 欧美大bbbb流白水| 国产鲁鲁视频在线观看| 欧美性猛交xxxx黑人| 熟女女同亚洲女同中文字幕| 国产精品一区二区久久精品| 91国产自拍一区二区三区| 國產尤物AV尤物在線觀看| 亚洲精品国产suv一区88| 无码福利写真片视频在线播放| 国产精品一区二区 尿失禁| 高清国产精品人妻一区二区| 亚洲av永久一区二区| 国产日韩一区二区四季| 免费国产高清在线精品一区| 免费无遮挡毛片中文字幕| 狠狠人妻久久久久久综合九色| 深夜在线观看免费av| 鲁一鲁一鲁一鲁一澡| 国产日产亚洲系列av| 制服jk白丝h无内视频网站| 图们市| 18岁日韩内射颜射午夜久久成人| 老熟妇乱子交视频一区| 精品免费看国产一区二区| 在线精品亚洲区一区二区| 99精品高清在线播放| 四虎精品永久在线视频| 色综合久久精品亚洲国产| 亚洲中文日韩一区二区三区| 亚洲中文一区二区av| 人妻日韩精品中文字幕| 动漫av纯肉无码av在线播放| 亚洲自拍偷拍激情视频| 野外少妇被弄到喷水在线观看| 偷拍美女厕所尿尿嘘嘘小便| 中文国产不卡一区二区| 九九九国产| 中文字幕精品亚洲二区| 熟女精品国产一区二区三区| 国产免费播放一区二区三区| 亚洲欧洲一区二区精品|