目录
一、什么是观察者模式
二、C#中观察者模式的实现
三、两种实现的用法
1、事件与委托
2、IObserver和IObservable
四、参考文献
一、什么是观察者模式
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式(Publish-Subscribe)、模型-视图(Model-View)模式、源监听器(Source-Listener)或者从属者模式,它是对象行为型模式。
- 模型(Model) -发布者是数据源,模型层, 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
- 视图(View) -监听者是视图层, 界面设计人员进行图形界面设计。
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
二、C#中观察者模式的实现
C#语言,其事件和委托本身就是观察者模式的基本实现。除此之外,属性修改通知以及属性依赖等也是观察者模式的用途之一,在WinForm或者WPF中,通常将集合类控件,绑定到集合上,当集合数据发生变化时,绑定的控件能够得到通知,并且能够自动刷新界面。
在C#中使用观察者模式,除了常用的event事件之外,还可以通过IObservable<T>和IObserver<T>两个接口来实现事件流模式,看起来有些复杂,但是这种方式有很多优点,并且很容易能跟Reactive Extensions框架配合。最后讲述了Observerable集合BindingList<T>和ObservableCollection<T>,他们不是线程安全的,使用的时候需要注意多线程读写的问题,这两个集合通常跟配套的支持集合的控件来绑定使用,这样能做到当集合数据发生变化时,对应的界面能够自动刷新。
在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。
C#的设计者通过关键字event来简化对观察者模式的使用。它的基本用法是,首先使用event关键字定义事件,然后注册事件回调方法EventHandler,回调方法通常包含两个参数,一个object类型的sender和一个继承自EventArgs的参数,该参数携带一些触发事件的必要信息。
三、两种实现的用法
1、事件与委托
event事件其实是对委托的包装。对event的包装成为EventHandler,有泛型和非泛型版本,泛型主要是继承自EventArgs的类型。具体使用可以了解如何使用event定义事件。并且由此了解Action Func的用法,加深对委托的理解。
2、IObserver<T>和IObservable<T>
如果将一个List<T>绑定到WinForm或者WPF中的ListBox,当List发送改变的时候,UI不会更新,这是因为List<T>并不支持Observer模式。当然如果单个对象发生改变,但整个List并没有事件能够向外通知其内容发送了改变。诚然我们可以对List进行包装,在Add或者Remove方法里添加事件通知,可以但没必要,在WinForm和WPF中都有Observable集合,他们分别是BindingList<T>和ObservableCollection<T>。
这两个集合类型表现起来就跟Collection<T>一样,但是他提供了额外的通知。比如,对于UI组件,当绑定的集合发送改变时,UI会自动进行更新。ObservableCollection<T>实现了INotifyCollectionChanged接口,所以他有CollectionChanged事件。这个时间会告知集合发生了什么改变,并且会提供旧的和新的元素,以及旧的和新的元素在集合中的位置,换句话说,根据事件我们能够正确的重新绘制ListBox或者其他集合组件。
需要注意的是BindingList<T>和ObservableCollection<T>都不是线程安全的集合,因此在多线程读写集合的时候,需要特别注意。一种方式是继承自Observable集合,然后对一些写入操作加锁。另外一种方法是继承自ConcurrentBag<T>,然后实现INotifyCollectionChanged接口。相对而言,第一种方式简单一些。
IObservable<T>和IObserver<T> 类型的代码
Weather weather = new Weather() { Teapreture=new WeatherData { temperature="25度"} };
Person wanglin = new Person() { Name="王玲"};
Person mulan = new Person() { Name = "木兰" };
weather.Subscribe(wanglin);///Subscribe 等同于 事件的"+"
weather.Subscribe(mulan);//
weather.NotifyTeapreture();//公告温度
Console.Read();
/// <summary>
/// 订阅者
/// </summary>
public class Person : IObserver<WeatherData>
{//关注温度public string Name { get; set; }public void OnCompleted(){Console.WriteLine(" ");}public void OnError(Exception error){throw new NotImplementedException();}public void OnNext(WeatherData value){Console.WriteLine($"我{Name}知道今天的天气了,是{value.temperature}");}
}
/// <summary>
/// 数据
/// </summary>
public class WeatherData
{/// <summary>/// 气温/// </summary>public string temperature { get; set; }/// <summary>/// 湿度/// </summary>public string humility { get; set; }/// <summary>/// 气压/// </summary>public string pressure { get; set; }
}
/// <summary>
/// 发布者
/// </summary>
public class Weather : IObservable<WeatherData>
{//提供温度 数据public WeatherData Teapreture { get; set; }private List<IObserver<WeatherData>> subsribers = new List<IObserver<WeatherData>>();public IDisposable Subscribe(IObserver<WeatherData> observer){if (!subsribers.Contains(observer)){subsribers.Add(observer);}return new UnSubsribe(this, observer);}class UnSubsribe : IDisposable{private Weather weather;private IObserver<WeatherData> observer;public UnSubsribe(Weather weather, IObserver<WeatherData> observer){this.weather = weather;this.observer = observer;}public void Dispose(){weather.subsribers.Remove(observer);}}public void NotifyTeapreture(){foreach (var item in subsribers){item.OnNext(Teapreture);}}public void OnComplete(){foreach (var item in subsribers){item.OnCompleted();}}}
四、参考文献
【C# 设计模式】观察者模式 - 小林野夫 - 博客园 (cnblogs.com)