C#内部有二个泛型接口,IObservable<T>和IObserver<T>,IObservable是可观察的,就是主题Subject要实现的接口,IObserver是观察者要实现的接口,首先建一个WeatherData结构,这是主题要向观察者传递的数据。
public struct WeatherData { private double temperature;//温度 private double humidity;//湿度 public double Temperature { get => temperature; } public double Humidity { get => humidity; } public WeatherData(double temp,double humidity) { this.temperature = temp; this.humidity = humidity; } }再建一个实现IObservable的类WeatherDataProvider,IObservable接口就一个很简单的方法 public IDisposable Subscribe(IObserver<T> observer),这个方法就是向内部的列表添加观察者,再返回一个可销毁的对象,所以要建一个私有的列表
//观察者列表 private List<IObserver<WeatherData>> observers;还要在实现一个内部类,这个类用于返回可销毁的对象,所以要实现IDisposable接口,这个内部类很简单,就是保存一个观察者列表的引用,一个观察者的引用,在Dispose方法内从观察者列表中删除观察者,完整代码如下
private class Unsubscriber : IDisposable { private List<IObserver<WeatherData>> _observers; private IObserver<WeatherData> _observer; public Unsubscriber(List<IObserver<WeatherData>> observers, IObserver<WeatherData> observer) { this._observers = observers; this._observer = observer; } public void Dispose() { if (_observer != null && _observers.Contains(_observer)) { _observers.Remove(_observer); } } }再实现方法Subscribe,向观察者列表添加观察者,再返回可销毁的内部类的一个实例。
public IDisposable Subscribe(IObserver<WeatherData> observer) { if(!observers.Contains(observer)) { observers.Add(observer); } return new Unsubscriber(observers, observer); }再添加一个向观察者发送数据的方法,如果发生错误,就生成一个异常,所以建一个异常类,为简单,这个异常类啥也不做。
public class WeatherDataUnkownException:Exception { }发送数据的方法
public void SendWeatherData(Nullable<WeatherData> weather) { foreach(var observer in observers) { if (!weather.HasValue) observer.OnError(new WeatherDataUnkownException()); else observer.OnNext(weather.Value); } }或许我们还需要一个结束发送数据的方法,以通知每个观察者。
public void EndTransmission() { foreach (var observer in observers.ToArray()) { //调用每个观察者OnCompleted方法 observer.OnCompleted(); } observers.Clear(); }完整代码如下
public class WeatherDataProvider : IObservable<WeatherData> { //观察者列表 private List<IObserver<WeatherData>> observers; public WeatherDataProvider() { observers = new List<IObserver<WeatherData>>(); } private class Unsubscriber : IDisposable { private List<IObserver<WeatherData>> _observers; private IObserver<WeatherData> _observer; public Unsubscriber(List<IObserver<WeatherData>> observers, IObserver<WeatherData> observer) { this._observers = observers; this._observer = observer; } public void Dispose() { if (_observer != null && _observers.Contains(_observer)) { _observers.Remove(_observer); } } } public IDisposable Subscribe(IObserver<WeatherData> observer) { if(!observers.Contains(observer)) { observers.Add(observer); } return new Unsubscriber(observers, observer); } public void SendWeatherData(Nullable<WeatherData> weather) { foreach(var observer in observers) { if (!weather.HasValue) observer.OnError(new WeatherDataUnkownException()); else observer.OnNext(weather.Value); } } public void EndTransmission() { foreach (var observer in observers.ToArray()) { //调用每个观察者OnCompleted方法 observer.OnCompleted(); } observers.Clear(); } }主题模型建好了,再建一个实现了IObserver<T>的观察者类,观察者类内部有保存一个私有的可销毁的对象private IDisposable unsubscriber,用于观察者取消订阅,每个观察者或许需要一个名称 private string instName。到今天我才明白,为什么叫观察者模式,观察者是变化的,除了要封装观察者外,订阅与取消订阅,主动权全在观察者,被观察者虽然保存有观察者列表,可以删除与添加,但并不主动用,被观察者只负责发送数据,这就是观察者模式。既然主动权全在观察者,所以观察者要建一个订阅的方法
public virtual void Subscribe(IObservable<WeatherData> provider) { if (provider != null) { unsubscriber = provider.Subscribe(this); } }用于取消订阅的方法
public virtual void Unsubscribe() { unsubscriber.Dispose(); }下面就是三个实现IObserver<WeatherData>的三个方法
public void OnCompleted() { Console.WriteLine($"天气数据发送完毕,不再向{instName}提供天气数据"); Unsubscribe(); } public void OnError(Exception error) { Console.WriteLine($"在向{instName}提供天气数据时,发生错误。"); } public void OnNext(WeatherData value) { Console.WriteLine($"{instName}: 温度:{value.Temperature} 湿度:{value.Humidity}"); }我们来实现看看效果
static void Main(string[] args) { WeatherDataProvider provider = new WeatherDataProvider(); WeatherDataReporter reporter1 = new WeatherDataReporter("东湖"); reporter1.Subscribe(provider); WeatherDataReporter reporter2 = new WeatherDataReporter("汉口"); reporter2.Subscribe(provider); WeatherDataReporter reporter3 = new WeatherDataReporter("汉阳"); reporter3.Subscribe(provider); provider.SendWeatherData(new WeatherData(23, 35)); Console.WriteLine("================================>"); reporter1.Unsubscribe(); provider.SendWeatherData(new WeatherData(28, 40)); Console.WriteLine("===============================>"); provider.EndTransmission(); Console.WriteLine("\n运行结束"); Console.ReadLine(); }
效果图
运行正常,很好的实现了观察者模式。