设计模式学习之“观察者模式” [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 ...
随机推荐
- python专题-异常处理(基础)
之前在学习python的时候有整理过python异常处理的文章,不够简单也不够完整,所以决定再整理一篇,算做补充. http://www.cnblogs.com/cmt110/p/7464748.ht ...
- python基础教程(九)
python异常 python用异常对象(exception object)来表示异常情况.遇到错误后,会引发异常.如果异常对象并未被处理或捕捉,程序就会用所谓的 回溯(Traceback, 一种错误 ...
- python基础教程(五)
字符串基本操作 所有标准的序列操作(索引.分片.乘法.判断成员资格.求长度.取最小值和最大值)对字符串同样适用,前面已经讲述的这些操作.但是,请注意字符串都是不可变的. 字符串的方法: 字符串从str ...
- Java IO 学习总结 学习手册总结
Java IO 是一套Java用来读写数据(输入和输出)的API.大部分程序都要处理一些输入,并由输入产生一些输出.Java为此提供了java.io包. 代码 github地址:https://git ...
- node.js的fs核心模块读写文件操作 -----由浅入深
node.js 里fs模块 常用的功能 实现文件的读写 目录的操作 - 同步和异步共存 ,有异步不用同步 - fs.readFile 都不能读取比运行内存大的文件,如果文件偏大也不会使用readFil ...
- ★RFC标准库_目录链接
RFC(Request For Comments)是一个国际标准化的数据库,记录了从计算机到互联网的海量标准协议.它是一个免费公开的IT标准文件分享平台,其内容也在不断增长,与时俱进.它与ISO等组织 ...
- 团队作业6——展示博客(Alpha)
一.团队简介 李永豪(PM):项目经理,后台开发人员,协调团队内部的工作及开发团队之间的工作 杨海亮:后台开发人员,测试人员,熟悉java语言,编写java代码 郑靖涛:后台开发人员,测试人员,安卓程 ...
- 201521123039《Java程序设计》 第六周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 1.2 可选:使用常规方法总结其他上课内容. 答:1.cl ...
- 201521123017 《Java程序设计》第6周学习总结
1. 本周学习总结 <> 2. 书面作业 Q1.clone方法 1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么? 1. ...
- 201521123072《java程序设计》第十周学习总结
201521123072<java程序设计>第十周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异 ...