设计模式:观察者模式(一)

  • 观察者模式定义了对象之间一个主题对多应多个观察者的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
  • 角色
    • 主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法
    • 观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
    • 具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体的观察者。
    • 具体观察者(Concrete Observer):具体观察者是实现观察者接口的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己添加到具体主题的集合中,使自己成为它的观察者,或让这个具体的主题将自己从具体的主题中的观察者列表中删除,使自己不再是它的观察者。
  • 一对多关系
    • 利用观察者模式,主题是具有状态的对象,并可以控制这些状态。也就是说,有“一个”具有状态的主题。另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有许多的观察者,依赖主题来告诉他们状态何时改变了。这就产生一个关系:“一个”主题对应“多个”观察者的关系。
  • Demo背景 
    • 设计一个气象观测站,测量温度、湿度、气压等,会有多种公告板如气温布告板,舒适度布告板,天气预报布告板等等。每当天气数据变化时,这些布告板的数据就需要相应自动更新。
  • 代码实现
    • 主题接口:ISubject
      public interface ISubject
      {
          void RegisterObserver(IObserver o);
          void RemoveObserver(IObserver o);
          void NotifyObserver();
      }
    • 观察者接口:IObserver
      public interface IObserver
      {
          void Update(float temp, float humidity, float pressure);
      }
    • 具体主题:WeatherData
      public class WeatherData:ISubject
      {
          private ArrayList observers;
          private float temperature;
          private float humidity;
          private float pressure;
          public WeatherData()
              {
                  observers = new ArrayList();
              }
          public void RegisterObserver(IObserver o)
          {
              observers.Add(o);
          }
      
          public void RemoveObserver(IObserver o)
          {
              int i = observers.IndexOf(o);
              if (i >= 0)
              {
                  observers.RemoveAt(i);
              }
          }
      
          public void NotifyObserver()
          {
              for (int i = 0; i < observers.Count; i++)
              {
                  IObserver observer = (IObserver)observers[i];
                  observer.Update(temperature, humidity, pressure);
              }
          }
          public void messurementsChanged()
              {
                  NotifyObserver();
              }
          public void SetMessureMents(float temperature, float humidity, float pressure)
              {
                  this.temperature = temperature;
                  this.humidity = humidity;
                  this.pressure = pressure;
                  messurementsChanged();
              }
      }

       

    • 具体观察者:CurrentConditionDisplay,ForecastDisplay
      public interface IDisplayElement
      {
          void Display();
      }
      public class CurrentConditionDisplay : IObserver, IDisplayElement
      {
          private float temperature;
          private float humidity;
          private float pressure;
          private ISubject weatherData;
          public void Display()
          {
              Console.WriteLine($"公告板1 当前天气 => temperature:{temperature},humidity:{humidity},pressure:{pressure}");
          }
      
          public void Update(float temperature, float humidity, float pressure)
          {
              this.temperature = temperature;
              this.humidity = humidity;
              this.pressure = pressure;
              Display();
          }
          public CurrentConditionDisplay(ISubject weatherData)
          {
              this.weatherData = weatherData;
              weatherData.RegisterObserver(this);
          }
      }
      public class ForecastDisplay : IObserver, IDisplayElement
      {
          private float temperature;
          private float humidity;
          private float pressure;
          private ISubject weatherData;
          public void Display()
          {
              Console.WriteLine($"公告板2 明天天气 => temperature:{temperature},humidity:{humidity},pressure:{pressure}");
          }
      
          public void Update(float temperature, float humidity, float pressure)
          {
              this.temperature = temperature;
              this.humidity = humidity;
              this.pressure = pressure;
              Display();
          }
          public ForecastDisplay(ISubject weatherData)
          {
              this.weatherData = weatherData;
              weatherData.RegisterObserver(this);
          }
      }
    • 测试代码与运行结果
      static void Main(string[] args)
      {
          WeatherData weatherData = new WeatherData();
          Console.WriteLine("--------公告板1加入观察者-------");
          CurrentConditionDisplay display1 = new CurrentConditionDisplay(weatherData);
          weatherData.SetMessureMents(10, 20, 30);
          Console.WriteLine("--------公告板2加入观察者-------");
          ForecastDisplay display2 = new ForecastDisplay(weatherData);
          weatherData.SetMessureMents(15, 25, 35);
          Console.WriteLine("--------公告板1退出观察者-------");
          weatherData.RemoveObserver(display1);
          weatherData.SetMessureMents(19, 29, 39);
      }
  • 优点
    • 具体主题和具体观察者是松耦合关系。由于主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(subject)接口的某个类的实例,但不需要知道具体是哪个类。
    • 观察模式满足“开-闭原则”。主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,我们就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题(Observer)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。
  • 源码地址: https://github.com/DonyGu/DesignPatterns
posted @ 2019-08-17 22:28  我是搞艺术的  阅读(254)  评论(0编辑  收藏  举报