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()
{ }
}
}