C#内置观察者模式(消息推送模式)
摘自:MSDN
对比邮局和报纸订阅客户的关系,属于一对多的关系,消息更新即向客户推送消息。
在.net框架中已经提供了预定义的观察者模式的接口。
泛型接口IObservable<T>
用来实现可观察者,IObserver<T>
用来实现观察者。T是提供数据的类。
在可观察者内只调用观察者内的来自接口的方法,在观察者内只调用可观察者内来自接口的方法。这样就将观察者和可观察者解耦了,防止彼此藕断丝连。
订阅的工作由订阅者发起public virtual void Subscribe(IObservable<Location> provider)
,订阅的完成是在可订阅者内部完成public virtual void Subscribe(IObservable<Location> provider)
,订阅者调用可订阅者的接口方法Subscribe。其他取消订阅,报错,完成传送等和此类似。
具体代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 内置观察者模式
{
class Program
{
static void Main(string[] args)
{
// 定义两个观察者和两个被观察者
var provider = new LocationTracker();
var reporter1 = new LocationReporter("FixedGPS");
reporter1.Subscribe(provider); // 观察者发起订阅,在可观察者内完成订阅
var reporter2 = new LocationReporter("MobileGPS");
reporter2.Subscribe(provider);
provider.TrackLocation(new Location(47.6456, -122.1312));
reporter1.Unsubscribe();
provider.TrackLocation(new Location(47.6677, -122.1199));
provider.TrackLocation(null);
provider.EndTransmission();
Console.ReadKey();
}
}
// 观察者
public class LocationReporter : IObserver<Location>
{
private IDisposable _unsubscriber; // 取消订阅类实例
public LocationReporter(string name)
{
Name = name;
}
// 观察者名称
public string Name { get; }
// 订阅
public virtual void Subscribe(IObservable<Location> provider)
{
if (provider != null)
_unsubscriber = provider.Subscribe(this);
}
// 完成数据传送,取消订阅,实现观察者接口的方法,在可观察者内调用
public virtual void OnCompleted()
{
Console.WriteLine($"The Location Tracker has completed transmitting data to {Name}.");
Unsubscribe();
}
// 发生错误,实现观察者接口的方法,在可观察者内调用
public virtual void OnError(Exception e)
{
Console.WriteLine($"{Name}: The location cannot be determined.");
}
// 接收订阅的数据,实现观察者接口的方法,在可观察者内调用
public virtual void OnNext(Location value)
{
Console.WriteLine($"{Name}: The current location is {value.Latitude}, {value.Longitude}");
}
// 取消订阅
public virtual void Unsubscribe()
{
_unsubscriber.Dispose();
}
}
// 可观察者
public class LocationTracker : IObservable<Location>
{
public LocationTracker()
{
_observers = new List<IObserver<Location>>();
}
private readonly List<IObserver<Location>> _observers;
// 订阅
public IDisposable Subscribe(IObserver<Location> observer)
{
if (!_observers.Contains(observer))
_observers.Add(observer);
return new Unsubscriber(_observers, observer); // 返回取消订阅类,传入参数为所有订阅者和当前订阅者
}
// 取消订阅
private class Unsubscriber : IDisposable
{
private readonly List<IObserver<Location>> _observers; // 所有订阅者
private readonly IObserver<Location> _observer; // 当前订阅者
public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
{
_observers = observers;
_observer = observer;
}
// 取消订阅
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}
public void TrackLocation(Location? loc)
{
foreach (var observer in _observers)
{
if (!loc.HasValue)
observer.OnError(new LocationUnknownException());
else
observer.OnNext(loc.Value);
}
}
// 取消所有订阅者
public void EndTransmission()
{
foreach (var observer in _observers.ToArray())
if (_observers.Contains(observer))
observer.OnCompleted();
_observers.Clear();
}
}
// 提供通知信息的对象
public struct Location
{
public Location(double latitude, double longitude)
{
this.Latitude = latitude;
this.Longitude = longitude;
}
public double Latitude { get; }
public double Longitude { get; }
}
// 定制错误类
public class LocationUnknownException : Exception
{
internal LocationUnknownException()
{ }
}
}