觀察者模式是常用的設(shè)計(jì)模式,在.net環(huán)境下粉捻,其運(yùn)行時(shí)庫(kù)為開(kāi)發(fā)者提供了IObservable<T>
和 IObserver<T>
接口妈踊,用于實(shí)現(xiàn)觀察者模式軟件設(shè)計(jì)巷波。
IObservable<T>
//
// 摘要:
// 定義基于推送的通知的提供程序瓷炮。
//
// 類(lèi)型參數(shù):
// T:
// 提供通知信息的對(duì)象葱色。
public interface IObservable<out T>
{
//
// 摘要:
// 通知提供程序:某觀察程序?qū)⒁邮胀ㄖ? //
// 參數(shù):
// observer:
// 要接收通知的對(duì)象。
//
// 返回結(jié)果:
// 使資源釋放的觀察程序的接口娘香。
IDisposable Subscribe(IObserver<T> observer);
}
注解Subscribe:
調(diào)用Subscribe通知提供程序某觀察程序?qū)⒁邮胀ㄖ醋?cè)苍狰、訂閱),不同于常規(guī)實(shí)現(xiàn)烘绽,它具有一個(gè)返回值淋昭,是一個(gè)IDisposable對(duì)象,當(dāng)觀察者不再接收通知時(shí)安接,可調(diào)用Dispose函數(shù)取消訂閱(反注冊(cè))翔忽,這種方法充分發(fā)揮C#語(yǔ)言的特性。
IObserver<in T>
//
// 摘要:
// 提供用于接收基于推送的通知的機(jī)制盏檐。
//
// 類(lèi)型參數(shù):
// T:
// 提供通知信息的對(duì)象歇式。
public interface IObserver<in T>
{
//
// 摘要:
// 通知觀察者,提供程序已完成發(fā)送基于推送的通知糯笙。
void OnCompleted();
//
// 摘要:
// 通知觀察者贬丛,提供程序遇到錯(cuò)誤情況。
//
// 參數(shù):
// error:
// 一個(gè)提供有關(guān)錯(cuò)誤的附加信息的對(duì)象给涕。
void OnError(Exception error);
//
// 摘要:
// 向觀察者提供新數(shù)據(jù)。
//
// 參數(shù):
// value:
// 當(dāng)前的通知信息额获。
void OnNext(T value);
}
示例
下面例子演示觀察者設(shè)計(jì)模式够庙,實(shí)現(xiàn)定位系統(tǒng)實(shí)時(shí)通知當(dāng)前經(jīng)緯度坐標(biāo)。
包含經(jīng)緯度坐標(biāo)的Locaiton結(jié)構(gòu)體
public struct Location
{
public Location(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
public double Latitude
{
get; private set;
}
public double Longitude
{
get;
private set;
}
}
LocationTracker 類(lèi)
實(shí)現(xiàn)了IObservable<T>
接口抄邀。
public class LocationTracker : IObservable<Location>
{
public LocationTracker()
{
observers = new List<IObserver<Location>>();
}
private List<IObserver<Location>> observers;
public IDisposable Subscribe(IObserver<Location> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
// 用于取消訂閱通知的IDisposable對(duì)象的實(shí)現(xiàn)
private class Unsubscriber : IDisposable
{
private List<IObserver<Location>> _observers;
private IObserver<Location> _observer;
public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}
// TrackLocation 方法傳遞了一個(gè)包含緯度和經(jīng)度數(shù)據(jù)的Location對(duì)象耘眨。
// 如果Location值不為null,則 TrackLocation 方法會(huì)調(diào)用每個(gè)觀察程序的 OnNext 方法境肾,
// 否則調(diào)用OnError方法
public void TrackLocation(Nullable<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();
}
}
LocationUnknownException類(lèi)
無(wú)法定位異常剔难,當(dāng)LocationTracker無(wú)法向它的觀察者提供定位時(shí),通過(guò)OnError方法通知觀察者當(dāng)前定位系統(tǒng)無(wú)法定位奥喻。
public class LocationUnknownException : Exception
{
internal LocationUnknownException()
{ }
}
LocationObserver類(lèi)
定位信息的觀察者偶宫,實(shí)現(xiàn)了IObserver<Location>
接口
public class LocationReporter : IObserver<Location>
{
private IDisposable unsubscriber;
private string instName;
public LocationReporter(string name)
{
this.instName = name;
}
public string Name
{ get { return this.instName; } }
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 {0}.", this.Name);
this.Unsubscribe();
}
public virtual void OnError(Exception e)
{
Console.WriteLine("{0}: The location cannot be determined.", this.Name);
}
public virtual void OnNext(Location value)
{
Console.WriteLine("{2}: The current location is {0}, {1}", value.Latitude, value.Longitude, this.Name);
}
// 取消訂閱
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
最后來(lái)看一下怎么使用這個(gè)定位系統(tǒng)
class Program2
{
static void Main(string[] args)
{
// Define a provider and two observers.
LocationTracker provider = new LocationTracker();
LocationReporter reporter1 = new LocationReporter("FixedGPS");
reporter1.Subscribe(provider);
LocationReporter 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();
}
}
注解
很多時(shí)候被觀察者(IObservable)向觀察者(IObserver)提供的數(shù)據(jù)并不像Location這樣簡(jiǎn)單的結(jié)構(gòu)體。
而是一個(gè)包含復(fù)雜數(shù)據(jù)的類(lèi)环鲤,通炒壳鳎可能是被觀察者本身,這種情況是允許的,即IObserver<T>
實(shí)現(xiàn)和 T 可以表示同一類(lèi)型吵冒。
這時(shí)候的實(shí)現(xiàn)變成下面的型式:
public class LocationTracker2 : IObservable<LocationTracker>
{
public IDisposable Subscribe(IObserver<LocationTracker> observer)
{
throw new NotImplementedException();
}
}
public class LocationReporter2 : IObserver<LocationTracker2>
{
public void OnCompleted()
{
throw new NotImplementedException();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnNext(LocationTracker2 value)
{
throw new NotImplementedException();
}
}