日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

行為型模式之觀察者模式

 頭號(hào)碼甲 2021-07-12

在現(xiàn)實(shí)世界中,許多對(duì)象并不是獨(dú)立存在的,其中一個(gè)對(duì)象的行為發(fā)生改變可能會(huì)導(dǎo)致一個(gè)或者多個(gè)其他對(duì)象的行為也發(fā)生改變。例如,某種商品的物價(jià)上漲時(shí)會(huì)導(dǎo)致部分商家高興,而消費(fèi)者傷心。

在軟件世界也是這樣,例如,事件模型中的事件源與事件處理者。所有這些,如果用觀察者模式來實(shí)現(xiàn)就非常方便。

定義與特點(diǎn)

觀察者(Observer)模式的定義:指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對(duì)象行為型模式。

觀察者模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下:

  • 降低了目標(biāo)與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系

  • 目標(biāo)與觀察者之間建立了一套觸發(fā)機(jī)制。

它的主要缺點(diǎn)如下:

  • 目標(biāo)與觀察者之間的依賴關(guān)系并沒有完全解除,而且有可能出現(xiàn)循環(huán)引用。

  • 當(dāng)觀察者對(duì)象很多時(shí),通知的發(fā)布會(huì)花費(fèi)很多時(shí)間,影響程序的效率。

結(jié)構(gòu)與實(shí)現(xiàn)

實(shí)現(xiàn)觀察者模式時(shí)要注意具體目標(biāo)對(duì)象和具體觀察者對(duì)象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來,這違反了面向?qū)ο蟮脑O(shè)計(jì)原則。

模式的結(jié)構(gòu)

觀察者模式的主要角色如下:

  • 抽象主題(Subject)角色:也叫抽象目標(biāo)類,它提供了一個(gè)用于保存觀察者對(duì)象的聚集類和增加、刪除觀察者對(duì)象的方法,以及通知所有觀察者的抽象方法。

  • 具體主題(Concrete Subject)角色:也叫具體目標(biāo)類,它實(shí)現(xiàn)抽象目標(biāo)中的通知方法,當(dāng)具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),通知所有注冊過的觀察者對(duì)象。

  • 抽象觀察者(Observer)角色:它是一個(gè)抽象類或接口,它包含了一個(gè)更新自己的抽象方法,當(dāng)接到具體主題的更改通知時(shí)被調(diào)用。

  • 具體觀察者(Concrete Observer)角色:實(shí)現(xiàn)抽象觀察者中定義的抽象方法,以便在得到目標(biāo)的更改通知時(shí)更新自身的狀態(tài)。

觀察者模式的結(jié)構(gòu)圖如圖所示:

模式的實(shí)現(xiàn)

觀察者模式的實(shí)現(xiàn)代碼如下:

class Program
{
    static void Main(string[] args)
    {
        Subject subject=new ConcreteSubject();
        IObserver obs1=new ConcreteObserver1();
        IObserver obs2=new ConcreteObserver2();
        subject.Add(obs1);
        subject.Add(obs2);
        subject.NotifyObserver();
        Console.Read();        
    }
}

//抽象目標(biāo)
public abstract class Subject
{
    protected List<IObserver> observers=new List<IObserver>();   
    //增加觀察者方法
    public void Add(IObserver observer)
    {
        observers.Add(observer);
    }    
    //刪除觀察者方法
    public void Remove(IObserver observer)
    {
        observers.Remove(observer);
    }   
    public abstract void NotifyObserver(); //通知觀察者方法
}

//具體目標(biāo)
public class ConcreteSubject : Subject
{
    public override  void NotifyObserver()
    {
        Console.WriteLine("具體目標(biāo)發(fā)生改變...");
        Console.WriteLine("--------------");       
       
        foreach (var obs in observers)
    {
            obs.Response(); 
    }        
    }          
}

//抽象觀察者
public interface IObserver
{
    void Response(); //反應(yīng)
}

//具體觀察者1
public class ConcreteObserver1 : IObserver
{
    public void Response()
    {
        Console.WriteLine("具體觀察者1作出反應(yīng)!");
    }
}

//具體觀察者1
public class ConcreteObserver2 : IObserver
{
    public void Response()
    {
        Console.WriteLine("具體觀察者2作出反應(yīng)!");
    }
}

程序運(yùn)行結(jié)果如下:

具體目標(biāo)發(fā)生改變...
--------------
具體觀察者1作出反應(yīng)!
具體觀察者2作出反應(yīng)!

應(yīng)用場景

通過前面的分析與應(yīng)用實(shí)例可知觀察者模式適合以下幾種情形:

  • 對(duì)象間存在一對(duì)多關(guān)系,一個(gè)對(duì)象的狀態(tài)發(fā)生改變會(huì)影響其他對(duì)象。

  • 當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一方面時(shí),可將這二者封裝在獨(dú)立的對(duì)象中以使它們可以各自獨(dú)立地改變和復(fù)用。

擴(kuò)展:.net中的IObservable和 IObserver接口

在.net環(huán)境下,其運(yùn)行時(shí)庫為開發(fā)者提供了IObservable和 IObserver接口,用于實(shí)現(xiàn)觀察者模式軟件設(shè)計(jì)。另外,ObservableCollection 類表示一個(gè)動(dòng)態(tài)數(shù)據(jù)集合,它可在添加、刪除項(xiàng)目或刷新整個(gè)列表時(shí)提供通知。

  • 提供者或主題,是將通知發(fā)送給觀察者的對(duì)象。 提供程序是實(shí)現(xiàn)IObservable接口的類或結(jié)構(gòu)。 提供者必須實(shí)現(xiàn)單個(gè)方法IObservable.Subscribe,該方法由希望從提供者接收通知的觀察者調(diào)用。

  • 觀察者,即從提供程序接收通知的對(duì)象。 觀察者是實(shí)現(xiàn) IObserver接口的類或結(jié)構(gòu)。 觀察者必須實(shí)現(xiàn)以下三個(gè)方法,這三個(gè)方法均由提供程序調(diào)用
    IObserver.OnNext,它向觀察者提供新信息或當(dāng)前信息。
    IObserver.OnError,它通知觀察者已發(fā)生錯(cuò)誤。
    IObserver.OnCompleted,它指示提供程序已完成發(fā)送通知。

  • 允許提供程序跟蹤觀察者的一種機(jī)制。 通常情況下,提供程序使用容器對(duì)象(如 List對(duì)象)來保存對(duì)已訂閱通知的 IObserver實(shí)現(xiàn)的引用。 將存儲(chǔ)容器用于此目的使提供程序能夠處理零到無限數(shù)量的觀察者。 未定義觀察者接收通知的順序;提供程序可以隨意使用任何方法來確定順序。

  • IDisposable 實(shí)現(xiàn),它使提供程序在能夠通知完成時(shí)刪除觀察者。 觀察者從 Subscribe 方法接收對(duì) IDisposable 實(shí)現(xiàn)的引用,因此它們還可以調(diào)用 IDisposable.Dispose 方法,以便在提供程序已完成發(fā)送通知之前取消訂閱。

  • 包含提供程序發(fā)送到其觀察者的數(shù)據(jù)的對(duì)象。 此對(duì)象的類型對(duì)應(yīng) IObservable和 IObserver接口的泛型類型參數(shù)。 盡管此對(duì)象可與 IObservable

    實(shí)現(xiàn)相同,但通常情況下,它是一個(gè)單獨(dú)的類型。

注:在 Java 中,通過 java.util.Observable 類和 java.util.Observer 接口定義了觀察者模式,只要實(shí)現(xiàn)它們的子類就可以編寫觀察者模式實(shí)例。

下面的示例演示觀察者設(shè)計(jì)模式,實(shí)現(xiàn)定位系統(tǒng)實(shí)時(shí)通知當(dāng)前經(jīng)緯度坐標(biāo),代碼如下:

class Program
{    
    static void Main(string[] args)
    {
        // 定義一個(gè)提供者和兩個(gè)觀察者
        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();

        Console.Read();        
    }
}

/// <summary>
/// 位置:包含緯度和經(jīng)度信息
/// </summary>
public struct Location
{
    double lat, lon;

    public Location(double latitude, double longitude)
    {
        this.lat = latitude;
        this.lon = longitude;
    }

    /// <summary>
    /// 緯度
    /// </summary>
    public double Latitude
    { get { return this.lat; } }
    /// <summary>
    /// 經(jīng)度
    /// </summary>
    public double Longitude
    { get { return this.lon; } }
}

/// <summary>
/// 位置報(bào)告者:提供 IObserver<T> 實(shí)現(xiàn),它顯示有關(guān)當(dāng)前控制臺(tái)位置的信息
/// </summary>
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; } }

    /// <summary>
    /// 訂閱:將由對(duì) Subscribe 的調(diào)用返回的 IDisposable 實(shí)現(xiàn)保存到私有變量中
    /// </summary>
    /// <param name="provider"></param>
    public virtual void Subscribe(IObservable<Location> provider)
    {
        if (provider != null)
            unsubscriber = provider.Subscribe(this);
    }

    public virtual void OnCompleted()
    {
        Console.WriteLine("位置跟蹤器已將數(shù)據(jù)傳輸?shù)?nbsp;{0}", this.Name);
        this.Unsubscribe();
    }

    public virtual void OnError(Exception e)
    {
        Console.WriteLine("{0}: 無法確定位置", this.Name);
    }

    public virtual void OnNext(Location value)
    {
        Console.WriteLine("{2}: 當(dāng)前位置是 {0}, {1}", value.Latitude, value.Longitude, this.Name);
    }

    /// <summary>
    /// 退訂:使類可以通過調(diào)用提供程序的 Dispose 實(shí)現(xiàn)來取消訂閱通知
    /// </summary>
    public virtual void Unsubscribe()
    {
        unsubscriber.Dispose();
    }
}

/// <summary>
/// 位置跟蹤器:提供 IObservable<T> 實(shí)現(xiàn)
/// </summary>
public class LocationTracker : IObservable<Location>
{
    public LocationTracker()
    {
        observers = new List<IObserver<Location>>();
    }

    private List<IObserver<Location>> observers;

    /// <summary>
    /// 訂閱:某觀察程序?qū)⒁邮胀ㄖ?    /// </summary>
    /// <param name="observer"></param>
    /// <returns></returns>
    public IDisposable Subscribe(IObserver<Location> observer)
    {
        if (!observers.Contains(observer))
            observers.Add(observer);
        return new Unsubscriber(observers, observer);
    }

    /// <summary>
    /// IDisposable 實(shí)現(xiàn):用于刪除觀察者或取消訂閱
    /// </summary>
    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);
        }
    }

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

/// <summary>
/// 位置未知異常
/// </summary>
public class LocationUnknownException : Exception
{
    internal LocationUnknownException()
    { }
}

程序運(yùn)行結(jié)果如下:

FixedGPS:當(dāng)前位置是47.6456,-122.1312
MobileGPS:當(dāng)前位置是47.6456,-122.1312
MobileGPS:當(dāng)前位置是47.6677,-122.1199
MobileGPS:無法確定位置位置
跟蹤器已將數(shù)據(jù)傳輸?shù)組obileGPS

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多