C#使用IObservable和IObserver實(shí)現(xiàn)觀察者模式

觀察者模式是常用的設(shè)計(jì)模式,在.net環(huán)境下,其運(yùn)行時(shí)庫為開發(fā)者提供了IObservable<T>IObserver<T>接口,用于實(shí)現(xiàn)觀察者模式軟件設(shè)計(jì)。

IObservable<T>

    //
    // 摘要:
    //     定義基于推送的通知的提供程序。
    //
    // 類型參數(shù):
    //   T:
    //     提供通知信息的對象。
    public interface IObservable<out T>
    {
        //
        // 摘要:
        //     通知提供程序:某觀察程序?qū)⒁邮胀ㄖ?        //
        // 參數(shù):
        //   observer:
        //     要接收通知的對象。
        //
        // 返回結(jié)果:
        //     使資源釋放的觀察程序的接口。
        IDisposable Subscribe(IObserver<T> observer);
    }

注解Subscribe:

調(diào)用Subscribe通知提供程序某觀察程序?qū)⒁邮胀ㄖ醋?、訂閱),不同于常?guī)實(shí)現(xiàn),它具有一個(gè)返回值,是一個(gè)IDisposable對象,當(dāng)觀察者不再接收通知時(shí),可調(diào)用Dispose函數(shù)取消訂閱(反注冊),這種方法充分發(fā)揮C#語言的特性。

IObserver<in T>

//
// 摘要:
//     提供用于接收基于推送的通知的機(jī)制。
//
// 類型參數(shù):
//   T:
//     提供通知信息的對象。
public interface IObserver<in T>
{
    //
    // 摘要:
    //     通知觀察者,提供程序已完成發(fā)送基于推送的通知。
    void OnCompleted();
    //
    // 摘要:
    //     通知觀察者,提供程序遇到錯(cuò)誤情況。
    //
    // 參數(shù):
    //   error:
    //     一個(gè)提供有關(guān)錯(cuò)誤的附加信息的對象。
    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 類
實(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對象的實(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對象。 
    // 如果Location值不為null,則 TrackLocation 方法會調(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類

無法定位異常,當(dāng)LocationTracker無法向它的觀察者提供定位時(shí),通過OnError方法通知觀察者當(dāng)前定位系統(tǒng)無法定位。

public class LocationUnknownException : Exception
{
   internal LocationUnknownException() 
   { }
}

LocationObserver類

定位信息的觀察者,實(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();
    }
}

最后來看一下怎么使用這個(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é)構(gòu)體。

而是一個(gè)包含復(fù)雜數(shù)據(jù)的類,通??赡苁潜挥^察者本身,這種情況是允許的,即IObserver<T> 實(shí)現(xiàn)和 T 可以表示同一類型。

這時(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();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容