设计模式学习之“观察者模式” [C#]
《深入浅出设计模式》学习笔记第二章
需求:
开发一套气象监测应用,如图:
气象站,目前有三种装置,温度、湿度和气压感应装置。
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#实现:
接口们:
namespace C02ObserverPattern.Raw.Bases { public interface ISubject { // 订阅 void RegisterObserver(IObserver observer); // 取消订阅 void RemoveObserver(IObserver observer); // 状态变化时,通知所有观察者 void NotifyObservers(); } public interface IObserver { // 气象之变化时,subject会把这些值更新给observers void Update(float temp, float humidity, float pressure); } public interface IDisplayElement { // 布告板显示 void Display(); } }
WeatherData(subject):
namespace C02ObserverPattern.Raw { public class WeatherData: ISubject { private readonly HashSet<IObserver> _observers; private float _temp, _humidity, _pressure; public WeatherData() { _observers = new HashSet<IObserver>(); } public void RegisterObserver(IObserver observer) { _observers.Add(observer); } public void RemoveObserver(IObserver observer) { _observers.Remove(observer); } public void NotifyObservers() { foreach (var observer in _observers) { observer.Update(_temp, _humidity, _pressure); } } public void MeasurementsChanged() { NotifyObservers(); } public void SetMeasurements(float temp, float humidity, float pressure) { _temp = temp; _humidity = humidity; _pressure = pressure; MeasurementsChanged(); } } }
其中一个布告板:
namespace C02ObserverPattern.Raw { public class CurrentConditionDisplay: IObserver, IDisplayElement { private float _temp, _humidity; public CurrentConditionDisplay(ISubject weatherData) { weatherData.RegisterObserver(this); } public void Update(float temp, float humidity, float pressure) { _temp = temp; _humidity = humidity; Display(); } // 这个布告板只显示温度和湿度 public void Display() { Console.WriteLine($"Current conditions:{_temp}℃ and {_humidity}% humidity"); } } }
测试程序:
namespace C02ObserverPattern { class Program { static void Main(string[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); // XxxDisplay1 xxxDisplay1 = new XxxDisplay1(weatherData); // XxxDisplay2 xxxDisplay2 = new XxxDisplay2(weatherData); weatherData.SetMeasurements(, , ); weatherData.SetMeasurements(, , ); weatherData.SetMeasurements(, , ); Console.ReadLine(); } } }
结果:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
使用C#内置的Observer Pattern
IObservable<T> 作为Subject。
IObserver<T> 作为Observer。
先封装一下数据:
namespace C02ObserverPattern.BuiltIn { public struct MyData { public float Temperature { get; } public float Humidity { get; } public float Pressure { get; } public MyData(float temperature, float humidity, float pressure) { Temperature = temperature; Humidity = humidity; Pressure = pressure; } } }
WeatherData:
namespace C02ObserverPattern.BuiltIn { public class WeatherDataAlternative : IObservable<MyData> { private readonly HashSet<IObserver<MyData>> _observers; public WeatherDataAlternative() { _observers = new HashSet<IObserver<MyData>>(); } public IDisposable Subscribe(IObserver<MyData> observer) { if (!_observers.Contains(observer)) { _observers.Add(observer); } return new Unsubscriber(_observers, observer); } public void NotifyMeasurementsChanged(MyData? data) { foreach (var observer in _observers) { if (!data.HasValue) { observer.OnError(new Exception("No value!!!")); } else { observer.OnNext(data.Value); } } } public void WeatherStationClose() { foreach (var observer in _observers.ToArray()) { observer.OnCompleted(); } _observers.Clear(); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The weather station has been closed."); } private class Unsubscriber : IDisposable { private readonly HashSet<IObserver<MyData>> _observers; private readonly IObserver<MyData> _observer; public Unsubscriber(HashSet<IObserver<MyData>> observers, IObserver<MyData> observer) { _observers = observers; _observer = observer; } public void Dispose() { if (_observer != null && _observers.Contains(_observer)) _observers.Remove(_observer); } } } }
实现两个布告板:
namespace C02ObserverPattern.BuiltIn { public class CurrentConditionDisplayAlternative : IObserver<MyData> { private IDisposable _unsubscriber; public virtual void Subscribe(IObservable<MyData> provider) { if (provider != null) _unsubscriber = provider.Subscribe(this); } public void OnNext(MyData value) { Console.WriteLine($"Current condition: {value.Temperature}C degrees and {value.Humidity}% humidity and {value.Pressure} pressure."); } public void OnError(Exception error) { var original = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error occurred at Current Condition Display!"); Console.ForegroundColor = original; } public void OnCompleted() { Console.WriteLine("Current condition display Task Completed."); Unsubscribe(); } public virtual void Unsubscribe() { Console.WriteLine("Current condition display will unsubscribe from weather station."); _unsubscriber.Dispose(); } } }
namespace C02ObserverPattern.BuiltIn { public class StatisticsDisplayAlternative : IObserver<MyData> { private IDisposable _unsubscriber; public virtual void Subscribe(IObservable<MyData> provider) { if (provider != null) _unsubscriber = provider.Subscribe(this); } public void OnNext(MyData value) { Console.WriteLine($"Statistics: {value.Temperature}, {value.Humidity}, {value.Pressure}."); } public void OnError(Exception error) { var original = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error occurred at Statistics!"); Console.ForegroundColor = original; } public void OnCompleted() { Console.WriteLine("Statistics Task Completed."); Unsubscribe(); } public virtual void Unsubscribe() { Console.WriteLine("Statistics will unsubscribe from weather station."); _unsubscriber.Dispose(); } } }
测试程序:
namespace C02ObserverPattern { class Program { static void Main(string[] args) { WeatherDataAlternative weatherDataAlternative = new WeatherDataAlternative(); CurrentConditionDisplayAlternative currentConditionDisplayAlternative = new CurrentConditionDisplayAlternative(); currentConditionDisplayAlternative.Subscribe(weatherDataAlternative); StatisticsDisplayAlternative statisticsDisplayAlternative = new StatisticsDisplayAlternative(); statisticsDisplayAlternative.Subscribe(weatherDataAlternative); weatherDataAlternative.NotifyMeasurementsChanged(, , )); Task.Delay(); weatherDataAlternative.NotifyMeasurementsChanged(null); Task.Delay(); currentConditionDisplayAlternative.Unsubscribe(); weatherDataAlternative.NotifyMeasurementsChanged(, , )); weatherDataAlternative.WeatherStationClose(); Console.ReadLine(); } } }
设计模式学习之“观察者模式” [C#]的更多相关文章
- Java设计模式学习记录-观察者模式
前言 观察者模式也是对象行为模式的一种,又叫做发表-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式. 咱们目前用的最多的就是各种MQ(Message Queue ...
- 【C++深入浅出】设计模式学习之观察者模式
前言 前两天学习了weak_ptr以后还是不甚明了,一则需要实际应用去锤炼,二来就是不懂观察者模式. 正文 观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对 ...
- 设计模式学习之观察者模式(Observer,行为型模式)(7)
1.观察者模式又叫做发布-订阅模式. 2.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 3 ...
- php设计模式学习之观察者模式
什么都不说,先看代码: interface userOperateImpl { public function operate($username); } class userLoginLog imp ...
- Java-马士兵设计模式学习笔记-观察者模式-模拟Awt Button
一.概述 Java 的Awt是 Observer模式,现用Java自己模拟awt中Button的运行机制 二.代码 1.Test.java import java.text.DateFormat; i ...
- Java-马士兵设计模式学习笔记-观察者模式-AWT简单例子
1.AWT简单例子 TestFrame.java import java.awt.Button; import java.awt.Frame; import java.awt.event.Action ...
- Java-马士兵设计模式学习笔记-观察者模式-读取properties文件改成单例模式
一.概述 1.目标:读取properties文件改成单例模式 二.代码 1.Test.java class WakenUpEvent{ private long time; private Strin ...
- Java-马士兵设计模式学习笔记-观察者模式-OOD 封装Listener
一.概述 childe类中的是关联监听者dad的,若要再增加监听者,会很不方便,且要修改代码.好的方法是封装监听者类,用addListener()方法动态添加监听者 二.代码 1.Test.java ...
- Java-马士兵设计模式学习笔记-观察者模式-OOD 封装event
把小孩醒来时的具体情况封装成事件类 Test.java class WakenUpEvent{ private long time; private String location; private ...
随机推荐
- solr-geohsah 按照距离搜索分组
通过solr的domain-import,将mysql的数据通过查询,导入到solr中.java通过使用solrj,链接solr,调用domaininport,并将分页参数设置到domain-impo ...
- 再起航,我的学习笔记之JavaScript设计模式17(模板方法模式)
模板方法模式 由模板方法模式开始我们正式告别结构型设计模式,开始行为型设计模式的学习分享 行为型设计模式用于不同对象之间职责划分或算法抽象,行为型设计模式不仅仅涉及类和对象,还涉及类或对象之间的交流模 ...
- tomcat设置文件编码
tomcat修改bin文件夹下面的catalina.bat文件可以解决乱码在文件中加上JAVA_OPTS="-server -Xms128M -Xmx4096M -XX:PermSize=5 ...
- String类的重要方法与字段
1.Length():获取当前字串长度 2.charAt(int index):获取当前字符串对象下标index处的字符 3.getChars():获取从指定位置起的子串复制到字符数组中 参数:int ...
- PHP初入,基础知识点整理(样式表&选择器的使用整理)
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- 201521123036 《Java程序设计》第8周学习总结
本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 HashMap自定义排序 List<Map.Entry<Stri ...
- 学号:201521123116 《java程序设计》第六周学习总结
1. 本章学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2.书面作业 1.1.Object对象中的clone方法是 ...
- 201521123055 《Java程序设计》第4周学习总结
1. 本章学习总结 2. 书面 Q1.注释的应用使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图) Q2.面向对象设计 2.1 将在网上商城购物或者在班级博客进行 ...
- 201521123040《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己 ...
- 201521123009《Java程序设计》第14周学习总结
1. 本周学习总结 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 在自己建立的数据库上执行常见SQL语句(截图) - ...