Define a one-to-many dependency between objects so that when oneobject changes state, all its dependents are notified and updatedautomatically..编程
— Design Patterns : Elements of Reusable Object-Oriented Software
观察者模式(Observer Pattern),又称为发布/订阅模式,它是软件设计模式中的一种。观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并被自动更新。设计模式
在观察者模式中,一个目标物件(被观察者)管理全部依赖于他的观察者,而且在它自己的状态发生改变时主动发出通知,这一般经过呼叫各个观察者所提供的方法来实现。dom
这种模式一般被用来实现事件处理系统。性能
观察者模式有不少实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察者。this
观察者和被观察者之间的互动关系不能是类之间的直接调用,那样就将观察者和被观察对象紧密耦合起来了,从而违反了面向对象设计原则。编码
经典观察者模式类图以下,在这个模型中,抽象的被被观察者(主题ISubject)有注册(attach)、取消注册(detech)、通知(notify)方法。
因为全部观察者均抽象成了IObserver,从而解除了主题与具体观察者之间的耦合。
spa
在事件处理场景,很天然会想到利用event 关键字 和EventHandler来实现观察者模式,这是一种最简单的方法。
同时C#也提供了一种低级抽象IObserver--IObservable(System命名空间,来自System.Runtime.dll) 来实现观察者。
被观察者(主题)接口:设计
namespace System { public interface IObservable<out T> { IDisposable Subscribe(IObserver<T> observer); } }
观察者接口:code
namespace System { public interface IObserver<in T> { void OnCompleted(); void OnError(Exception error); void OnNext(T value); } }
上述接口中,由被观察者将观察者注册到观察者列表,观察者分别有完成、失败、执行的动做。server
本文将基于一个经典案例分别用event 和IObserver 实现观察者模式。
场景描述:
气象部门根据气象卫星获取温度信息,当温度超过某一阈值时,须要向各单位发出高温预警通知,以便其及时作好高温防御错误。
在这个场景中,发布者是预警系统,观察者是各个单位。
抽象的模型图以下:
发布者(主题)的定义:
public class Subject : IObservable<decimal> { /// <summary> /// 观察者处理委托 /// </summary> public event EventHandler<decimal> Observers; /* * 高温黄色预警 >=35,<37 * 高温橙色预警 >=37,<40 * 高温红色预警 >=40 * **/ public void SetTemperature(decimal temperature) { if (temperature >= 35) { if (temperature >= 40) { _warningLevel = "红色"; } else if (temperature >= 37) { _warningLevel = "橙色"; } PublishWarning(temperature, _warningLevel); } } private void PublishWarning(decimal temperature, string warningLevel) { Console.WriteLine($"===========气象部门发布高温{_warningLevel} 预警,气温:{temperature} "); Observers?.Invoke(this, temperature); } }
分别定义三个观察者
/// <summary> /// 企业单位观察者 /// </summary> public class EnterpriseObserver { public void OnWarning(object sender,decimal eventArgs) { Console.WriteLine($"企业单位收到预警事件,气温:{eventArgs}"); } } /// <summary> /// 政府部门观察者 /// </summary> public class EnterpriseObserver { public void OnWarning(object sender, decimal eventArgs) { Console.WriteLine($"政府部门收到预警事件,气温:{eventArgs}"); } } /// <summary> /// 我的观察者 /// </summary> public class EnterpriseObserver { public void OnWarning(object sender, decimal eventArgs) { Console.WriteLine($"我的收到预警事件,气温:{eventArgs}"); } }
客户端调用,利用委托能够多播的特性增长多个观察者:
var subject = new Subject(); subject.Observers += new EnterpriseObserver().OnWarning; subject.Observers += new GovernmentObserver().OnWarning; subject.Observers += new PersonObserver().OnWarning; int t = 3; var random = new Random(500); while (t > 0) { var temperature = random.NextDouble() * 50; if (temperature > 35) { subject.SetTemperature((decimal)temperature); t--; } }
在上面的代码中进行改造
发布者(主题)的定义:
public class Unsubscriber<T> : IDisposable { private List<IObserver<T>> _observers; private IObserver<T> _observer; public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer) { _observers = observers; _observer = observer; } public void Dispose() { Console.WriteLine("Unsubscribed...."); _observers.Remove(_observer); } } /// <summary> /// 发布者 /// </summary> public class Subject : IObservable<decimal> { /// <summary> /// 观察者列表 /// </summary> private List<IObserver<decimal>> observers; /// <summary> /// 观察者处理委托 /// </summary> public event EventHandler<decimal> Observers; private decimal _temperature; private string _warningLevel; public Subject() { observers = new List<IObserver<decimal>>(); } public IDisposable Subscribe(IObserver<decimal> observer) { if (!observers.Contains(observer)) observers.Add(observer); return new Unsubscriber<decimal>(observers, observer); } /* * 高温黄色预警 >=35,<37 C * 高温橙色预警 >=37,<40 C * 高温红色预警 >=40 C * **/ public void SetTemperature(decimal temperature) { if (temperature >= 35) { if (temperature >= 40) { _warningLevel = "红色"; } else if (temperature >= 37) { _warningLevel = "橙色"; } PublishWarning(temperature, _warningLevel); } } private void PublishWarning(decimal temperature, string warningLevel) { Console.WriteLine($"===========气象部门发布高温{_warningLevel} 预警,气温:{temperature} "); foreach (var observer in observers) { observer.OnNext(temperature); } Observers?.Invoke(this, temperature); } }
观察者定义:
/// <summary> /// 企业单位观察者 /// </summary> public class EnterpriseObserver : IObserver<decimal> { public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(decimal value) { Console.WriteLine($"企业单位收到预警信息,气温:{value}"); } public void OnWarning(object sender,decimal eventArgs) { Console.WriteLine($"企业单位收到预警事件,气温:{eventArgs}"); } } /// <summary> /// 政府部门 /// </summary> public class GovernmentObserver : IObserver<decimal> { public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(decimal value) { Console.WriteLine($"政府部门收到预警信息,气温:{value}"); } public void OnWarning(object sender, decimal eventArgs) { Console.WriteLine($"政府部门收到预警事件,气温:{eventArgs}"); } } /// <summary> /// 我的观察者 /// </summary> public class PersonObserver : IObserver<decimal> { public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(decimal value) { Console.WriteLine($"我的收到预警信息,气温:{value}"); } public void OnWarning(object sender, decimal eventArgs) { Console.WriteLine($"我的收到预警事件,气温:{eventArgs}"); } }
客户端调用:
var subject = new Subject(); var ob1 = new EnterpriseObserver(); var ob2 = new GovernmentObserver(); var ob3 = new PersonObserver(); subject.Subscribe(ob1); subject.Subscribe(ob2); subject.Subscribe(ob3); int t = 3; var random = new Random(500); while (t > 0) { var temperature = random.NextDouble()*50; if (temperature > 35) { subject.SetTemperature((decimal)temperature); t--; } }