1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 设计模式学习(二): 观察者模式 (C#)

设计模式学习(二): 观察者模式 (C#)

时间:2024-02-01 15:53:12

相关推荐

设计模式学习(二): 观察者模式 (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会把这些值更新给observersvoid 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(10, 20, 30);weatherData.SetMeasurements(14, 25, 36);weatherData.SetMeasurements(40, 50, 60);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);}}}// 关闭这个Subjectpublic 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);}// 相当于Display()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(new MyData(25, 75, 120));Task.Delay(1000);weatherDataAlternative.NotifyMeasurementsChanged(null);Task.Delay(1000);currentConditionDisplayAlternative.Unsubscribe();weatherDataAlternative.NotifyMeasurementsChanged(new MyData(23, 45, 104));weatherDataAlternative.WeatherStationClose();Console.ReadLine();}}}

下面是我的关于 Core Web API相关技术的公众号--草根专栏:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。