《深入浅出设计模式》学习笔记第二章

需求:

开发一套气象监测应用,如图:

气象站,目前有三种装置,温度、湿度和气压感应装置。

WeatherData对象追踪气象站的数据,并更新到布告板,布告板(目前是三个:目前状况、气象统计、天气预报)用来显示目前的天气状况给用户。

初步设计

目前的要求:

1.其中有三个方法分别获得气温、湿度和气压的数据。

2.一旦气象测量被更新,那么这个measurementsChanged()方法就会被调用。

3.一旦有新的数据,者三个布告板(暂时三个)就会马上更新。

4.可扩展,可以开发第三方的布告板。

错误的实现:

错误在哪:

1.变化的地方需要封装。

2.布告板应该统一实现某个带有update方法的接口。

3.不应该针对实现编程,应该针对接口编程。

什么是观察者模式 Observer Pattern

例子:

我们订阅公众号,公众号一旦有新文章就会发送给我们。

当我不再想看文章时,就取消订阅,这时就不会给我发送文章了。

只要公众号还在运营,就一直有人订阅或者取消订阅。

出版者(Publishers) + 订阅者(Subscribers) = 观察者模式(Observer Pattern)

不过我们用的名词不一样,出版者改为主题(Subject),订阅者改为观察者(Observer)

观察者模式定义:

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者(dependents)都会收到通知并自动更新。

观察这模式关系图:

松耦合

两个对象之间的松耦合就是,他们可以交互,但是不清楚对方太多细节。

观察者模式的Subject与Observers之间是松耦合,因为:

1.Subject对于Observer知道的唯一一件事情就是它实现了某个接口。

2.随时可以添加新的Observer。

3.新的Observer出现时,永远不需要更改Subject的代码。

4.我们可以独立的复用Subject或Observer。

5.改变任意的一方并不会影响另外一方。

设计原则

交互对象之间应该尽可能的去实现松耦合设计。

气象应用的具体设计:

C#实现:

接口们:

  1. namespace C02ObserverPattern.Raw.Bases
  2. {
  3. public interface ISubject
  4. {
  5. // 订阅
  6. void RegisterObserver(IObserver observer);
  7.  
  8. // 取消订阅
  9. void RemoveObserver(IObserver observer);
  10.  
  11. // 状态变化时,通知所有观察者
  12. void NotifyObservers();
  13. }
  14.  
  15. public interface IObserver
  16. {
  17. // 气象之变化时,subject会把这些值更新给observers
  18. void Update(float temp, float humidity, float pressure);
  19. }
  20.  
  21. public interface IDisplayElement
  22. {
  23. // 布告板显示
  24. void Display();
  25. }
  26. }

WeatherData(subject):

  1. namespace C02ObserverPattern.Raw
  2. {
  3. public class WeatherData: ISubject
  4. {
  5. private readonly HashSet<IObserver> _observers;
  6. private float _temp, _humidity, _pressure;
  7.  
  8. public WeatherData()
  9. {
  10. _observers = new HashSet<IObserver>();
  11. }
  12.  
  13. public void RegisterObserver(IObserver observer)
  14. {
  15. _observers.Add(observer);
  16. }
  17.  
  18. public void RemoveObserver(IObserver observer)
  19. {
  20. _observers.Remove(observer);
  21. }
  22.  
  23. public void NotifyObservers()
  24. {
  25. foreach (var observer in _observers)
  26. {
  27. observer.Update(_temp, _humidity, _pressure);
  28. }
  29. }
  30.  
  31. public void MeasurementsChanged()
  32. {
  33. NotifyObservers();
  34. }
  35.  
  36. public void SetMeasurements(float temp, float humidity, float pressure)
  37. {
  38. _temp = temp;
  39. _humidity = humidity;
  40. _pressure = pressure;
  41. MeasurementsChanged();
  42. }
  43. }
  44. }

其中一个布告板:

  1. namespace C02ObserverPattern.Raw
  2. {
  3. public class CurrentConditionDisplay: IObserver, IDisplayElement
  4. {
  5. private float _temp, _humidity;
  6.  
  7. public CurrentConditionDisplay(ISubject weatherData)
  8. {
  9. weatherData.RegisterObserver(this);
  10. }
  11. public void Update(float temp, float humidity, float pressure)
  12. {
  13. _temp = temp;
  14. _humidity = humidity;
  15. Display();
  16. }
  17.  
  18. // 这个布告板只显示温度和湿度
  19. public void Display()
  20. {
  21. Console.WriteLine($"Current conditions:{_temp}℃ and {_humidity}% humidity");
  22. }
  23. }
  24. }

测试程序:

  1. namespace C02ObserverPattern
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. WeatherData weatherData = new WeatherData();
  8.  
  9. CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
  10. StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
  11. // XxxDisplay1 xxxDisplay1 = new XxxDisplay1(weatherData);
  12. // XxxDisplay2 xxxDisplay2 = new XxxDisplay2(weatherData);
  13.  
  14. weatherData.SetMeasurements(, , );
  15. weatherData.SetMeasurements(, , );
  16. weatherData.SetMeasurements(, , );
  17.  
  18. Console.ReadLine();
  19. }
  20. }
  21. }

结果:

----------------------------------------------------------------------------------------------------------------------------------------------------------------

使用C#内置的Observer Pattern

IObservable<T> 作为Subject。

IObserver<T> 作为Observer。

先封装一下数据:

  1. namespace C02ObserverPattern.BuiltIn
  2. {
  3. public struct MyData
  4. {
  5. public float Temperature { get; }
  6. public float Humidity { get; }
  7. public float Pressure { get; }
  8.  
  9. public MyData(float temperature, float humidity, float pressure)
  10. {
  11. Temperature = temperature;
  12. Humidity = humidity;
  13. Pressure = pressure;
  14. }
  15. }
  16. }

WeatherData:

  1. namespace C02ObserverPattern.BuiltIn
  2. {
  3. public class WeatherDataAlternative : IObservable<MyData>
  4. {
  5. private readonly HashSet<IObserver<MyData>> _observers;
  6.  
  7. public WeatherDataAlternative()
  8. {
  9. _observers = new HashSet<IObserver<MyData>>();
  10. }
  11.  
  12. public IDisposable Subscribe(IObserver<MyData> observer)
  13. {
  14. if (!_observers.Contains(observer))
  15. {
  16. _observers.Add(observer);
  17. }
  18. return new Unsubscriber(_observers, observer);
  19. }
  20.  
  21. public void NotifyMeasurementsChanged(MyData? data)
  22. {
  23. foreach (var observer in _observers)
  24. {
  25. if (!data.HasValue)
  26. {
  27. observer.OnError(new Exception("No value!!!"));
  28. }
  29. else
  30. {
  31. observer.OnNext(data.Value);
  32. }
  33. }
  34. }
  35.  
  36. public void WeatherStationClose()
  37. {
  38. foreach (var observer in _observers.ToArray())
  39. {
  40. observer.OnCompleted();
  41. }
  42. _observers.Clear();
  43. Console.ForegroundColor = ConsoleColor.Red;
  44. Console.WriteLine("The weather station has been closed.");
  45. }
  46.  
  47. private class Unsubscriber : IDisposable
  48. {
  49. private readonly HashSet<IObserver<MyData>> _observers;
  50. private readonly IObserver<MyData> _observer;
  51.  
  52. public Unsubscriber(HashSet<IObserver<MyData>> observers, IObserver<MyData> observer)
  53. {
  54. _observers = observers;
  55. _observer = observer;
  56. }
  57.  
  58. public void Dispose()
  59. {
  60. if (_observer != null && _observers.Contains(_observer))
  61. _observers.Remove(_observer);
  62. }
  63. }
  64.  
  65. }
  66. }

实现两个布告板

  1. namespace C02ObserverPattern.BuiltIn
  2. {
  3. public class CurrentConditionDisplayAlternative : IObserver<MyData>
  4. {
  5. private IDisposable _unsubscriber;
  6.  
  7. public virtual void Subscribe(IObservable<MyData> provider)
  8. {
  9. if (provider != null)
  10. _unsubscriber = provider.Subscribe(this);
  11. }
  12.  
  13. public void OnNext(MyData value)
  14. {
  15. Console.WriteLine($"Current condition: {value.Temperature}C degrees and {value.Humidity}% humidity and {value.Pressure} pressure.");
  16. }
  17.  
  18. public void OnError(Exception error)
  19. {
  20. var original = Console.ForegroundColor;
  21. Console.ForegroundColor = ConsoleColor.Red;
  22. Console.WriteLine("Error occurred at Current Condition Display!");
  23. Console.ForegroundColor = original;
  24. }
  25.  
  26. public void OnCompleted()
  27. {
  28. Console.WriteLine("Current condition display Task Completed.");
  29. Unsubscribe();
  30. }
  31.  
  32. public virtual void Unsubscribe()
  33. {
  34. Console.WriteLine("Current condition display will unsubscribe from weather station.");
  35. _unsubscriber.Dispose();
  36. }
  37. }
  38. }
  1. namespace C02ObserverPattern.BuiltIn
  2. {
  3. public class StatisticsDisplayAlternative : IObserver<MyData>
  4. {
  5. private IDisposable _unsubscriber;
  6.  
  7. public virtual void Subscribe(IObservable<MyData> provider)
  8. {
  9. if (provider != null)
  10. _unsubscriber = provider.Subscribe(this);
  11. }
  12.  
  13. public void OnNext(MyData value)
  14. {
  15. Console.WriteLine($"Statistics: {value.Temperature}, {value.Humidity}, {value.Pressure}.");
  16. }
  17.  
  18. public void OnError(Exception error)
  19. {
  20. var original = Console.ForegroundColor;
  21. Console.ForegroundColor = ConsoleColor.Red;
  22. Console.WriteLine("Error occurred at Statistics!");
  23. Console.ForegroundColor = original;
  24. }
  25.  
  26. public void OnCompleted()
  27. {
  28. Console.WriteLine("Statistics Task Completed.");
  29. Unsubscribe();
  30. }
  31.  
  32. public virtual void Unsubscribe()
  33. {
  34. Console.WriteLine("Statistics will unsubscribe from weather station.");
  35. _unsubscriber.Dispose();
  36. }
  37. }
  38. }

测试程序:

  1. namespace C02ObserverPattern
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. WeatherDataAlternative weatherDataAlternative = new WeatherDataAlternative();
  8. CurrentConditionDisplayAlternative currentConditionDisplayAlternative = new CurrentConditionDisplayAlternative();
  9. currentConditionDisplayAlternative.Subscribe(weatherDataAlternative);
  10. StatisticsDisplayAlternative statisticsDisplayAlternative = new StatisticsDisplayAlternative();
  11. statisticsDisplayAlternative.Subscribe(weatherDataAlternative);
  12.  
  13. weatherDataAlternative.NotifyMeasurementsChanged(, , ));
  14. Task.Delay();
  15. weatherDataAlternative.NotifyMeasurementsChanged(null);
  16. Task.Delay();
  17. currentConditionDisplayAlternative.Unsubscribe();
  18. weatherDataAlternative.NotifyMeasurementsChanged(, , ));
  19. weatherDataAlternative.WeatherStationClose();
  20.  
  21. Console.ReadLine();
  22. }
  23. }
  24. }

设计模式学习之“观察者模式” [C#]的更多相关文章

  1. Java设计模式学习记录-观察者模式

    前言 观察者模式也是对象行为模式的一种,又叫做发表-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式. 咱们目前用的最多的就是各种MQ(Message Queue ...

  2. 【C++深入浅出】设计模式学习之观察者模式

    前言 前两天学习了weak_ptr以后还是不甚明了,一则需要实际应用去锤炼,二来就是不懂观察者模式. 正文 观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对 ...

  3. 设计模式学习之观察者模式(Observer,行为型模式)(7)

    1.观察者模式又叫做发布-订阅模式. 2.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 3 ...

  4. php设计模式学习之观察者模式

    什么都不说,先看代码: interface userOperateImpl { public function operate($username); } class userLoginLog imp ...

  5. Java-马士兵设计模式学习笔记-观察者模式-模拟Awt Button

    一.概述 Java 的Awt是 Observer模式,现用Java自己模拟awt中Button的运行机制 二.代码 1.Test.java import java.text.DateFormat; i ...

  6. Java-马士兵设计模式学习笔记-观察者模式-AWT简单例子

    1.AWT简单例子 TestFrame.java import java.awt.Button; import java.awt.Frame; import java.awt.event.Action ...

  7. Java-马士兵设计模式学习笔记-观察者模式-读取properties文件改成单例模式

    一.概述 1.目标:读取properties文件改成单例模式 二.代码 1.Test.java class WakenUpEvent{ private long time; private Strin ...

  8. Java-马士兵设计模式学习笔记-观察者模式-OOD 封装Listener

    一.概述 childe类中的是关联监听者dad的,若要再增加监听者,会很不方便,且要修改代码.好的方法是封装监听者类,用addListener()方法动态添加监听者 二.代码 1.Test.java ...

  9. Java-马士兵设计模式学习笔记-观察者模式-OOD 封装event

    把小孩醒来时的具体情况封装成事件类 Test.java class WakenUpEvent{ private long time; private String location; private ...

随机推荐

  1. 第一行代码_activity生命周期

    这类文章基本上已经是烂大街了,不过我今天仍然要给自己做一遍梳理,因为通过昨天的项目我发现自己还是不太懂activity的各个生命周期;各位看官勿喷; 七个生命周期及其作用 oncreat 完成初始化操 ...

  2. HTTP手记

    ---------------------tcp/ip模型和osi模型---------------------tcp/ip协议模型   osi模型应用层   应用层 表示层 会话层传输层   传输层 ...

  3. firebird常用语句

    分页写法小例 SELECT FIRST templateid,code,name FROM template ; SELECT FIRST SKIP templateid,code,name FROM ...

  4. Keil提示premature end of file错误 无法生成HEX文件

    今天舍友在使用Keil UV4的时候遇到一个问题:Keil提示premature end of file,无法生成hex文件. 代码是没有错误的.那么问题就出在设置上面了. 百度了一圈,发现很少人解答 ...

  5. C++类静态成员与类静态成员函数

       当将类的某个数据成员声明为static时,该静态数据成员只能被定义一次,而且要被同类的所有对象共享.各个对象都拥有类中每一个普通数据成员的副本,但静态数据成员只有一个实例存在,与定义了多少类对象 ...

  6. 我的项目经验总结——CDN镜像:1(初探)

    前言 其实,这个标题有些大,作为一个小白,只是在实际工作中经常听闻我司的CDN服务如何如何牛B……而且我司的云服务还拿到了工信部的CDN牌照……那么,作为一个研发仔,怎么能不去了解和熟悉呢?!不过,这 ...

  7. 面向对象五大原则(SRP、OCP、LSP、DIP、ISP)

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt173 OO的五大原则是指 1. SRP(Single Responsibil ...

  8. 微信公众平台接口调用第一步(获取access_token)

    最近公司需要开发微信公众号,闲着无聊就写写博客,希望能帮到你我 上代码: package test; import java.util.List; import java.util.ArrayList ...

  9. JQuery实用技巧--学会你也是大神(1)——插件的制作技巧

      前  言 JRedu 学习之前,首先我们需要知道什么是JQuery? JQuery是一个优秀的javascript框架. JQuery是继Prototype之后又一个优秀的Javascript框架 ...

  10. EIGRP系统复习【转载】

    EIGRP理论 简介 EIGRP是Cisco私有协议,它是由距离矢量和链路状态两种路由协议混合而成的一种协议.即像距离矢量协议那样,EIGRP从它的相邻路由器那里得到更新信息:也像链路状态协议那样,保 ...