.NET 4.0下的觀察者模式


下圖是一個經典的觀察者模式的結構。(圖來源於wikipedia)

觀察者模式被定義為解決一個對象對多個對象的依賴問題。當一個對象的狀態發生改變,它會自動通知其它依賴對象。關於該設計模式的介紹可以更多參考Terrylee的.NET設計模式(19):觀察者模式(Observer Pattern)。這篇文章主要介紹在 .NET 4.0 怎么實現觀察者模式。

 

在.NET 4.0下位我們提供了兩個觀察者模式的底層接口:IObservable<T>和IObserver<T>。這兩個接口定義如下:

public interface IObservable<out T>
{
    IDisposable Subscribe(IObserver observer);
}

 

public interface IObserver<in T>
{
    void OnCompleted();
    void OnError(Exception error);
    void OnNext(T value);
}

 

IObserable就相當於是傳統模式中的Subject,而IObserver就是訂閱者。現在來演示一個新聞訂閱的實例:當有被訂閱的新聞站點有新文章發布時,發送信息通知它的訂閱者。

首先定義一個Article類,它就相當於是新聞網站中每一篇文章實體,保存着每篇文章的信息:

class Article
{
   public String Title;
   public DateTime Date;
   public String Author;
   public String Content;
}

 

然后是我們的訂閱源——新聞站點:

class News : IObservable<Article>
{
    //用一個list來模擬保存站點中所有的文章
    private List<Article> articles = new List<Article>();
    //保存的所有的訂閱者信息
    private List<IObserver<Article>> observers = new List<IObserver<Article>>();

    public IDisposable Subscribe(IObserver<Article> observer)
    {
        if (!this.observers.Contains(observer))
        {
            this.observers.Add(observer);
        }
        return new unsubscribe(observers, observer);
    }

    private class unsubscribe : IDisposable
    {
        private IObserver<Article> _observer;
        private List<IObserver<Article>> _observers;

        public unsubscribe(List<IObserver<Article>> observers, IObserver<Article> observer)
        {
            this._observer = observer;
            this._observers = observers;
        }

        public void Dispose()
        {
            if (this._observers != null && this._observers.Contains(this._observer))
            {
                this._observers.Remove(this._observer);
            }
        }
    }

    //向訂閱者發送通知
    private void Notify(Article article)
    {
        foreach (var item in this.observers)
        {
            item.OnNext(article);
        }
    }
    //當有新的文章發布時通知訂閱者
    public void AddArticle(Article article)
    {
        this.articles.Add(article);
        this.Notify(article);
    }

}

 

下面來定義我們的訂閱者。

class Person : IObserver<Article>
{
    //訂閱者姓名
    String name;
    //保存訂閱的新聞站點,方便以后取消訂閱
    Dictionary<IObservable<Article>, IDisposable> sbscribes = new Dictionary<IObservable<Article>, IDisposable>();

    public Person(String name)
    {
        this.name = name;
    }

    public void Subscribe(IObservable<Article> news)
    {
        IDisposable unsub = news.Subscribe(this);
        sbscribes[news] = unsub;
    }

    public void UnSubscribe(IObservable<Article> news)
    {
        if (sbscribes.ContainsKey(news))
        {
            sbscribes[news].Dispose();
            sbscribes.Remove(news);
        }
    }

    //沒有實現
    public void OnCompleted()
    {
        throw new NotImplementedException();
    }

    //當訂閱中出現錯誤的處理沒有實現
    public void OnError(Exception error)
    {
        throw new NotImplementedException();
    }

    //當有新的推送通知到達后的處理
    public void OnNext(Article value)
    {
        Console.WriteLine("One News: {0}",value.Title);
    }
}

 

下面我們看下運行時的代碼:

static void Main(string[] args)
{
    News ChinaNews = new News();
    News EuropeNews = new News();
    Person person1 = new Person("heqichang");
    Person person2 = new Person("Jim");

    //我自己訂閱了中國新聞和歐洲新聞
    person1.Subscribe(ChinaNews);
    person1.Subscribe(EuropeNews);
    //Jim訂閱了歐洲新聞
    person2.Subscribe(EuropeNews);

    EuropeNews.AddArticle(new Article()
    {
        Title = "New title",
        Date = DateTime.Now.Date,
        Author = "heqichang",
        Content = "New content"
    });

}

 

現在我們很多人都應該使用着智能手機,我們可能經常在手機上收到一些應用發來的推送信息,這樣的模式就跟我們的觀察者模式很像。  

 

擴展閱讀:

Reactive Extensions,簡稱Rx,是以IObserable<T>以及IObserver<T>為核心的,使用LINQ方式編程的.NET擴展庫。 Rx = Observables + LINQ + Schedulers。有興趣的可以搜索一下。

以前我們訂閱博客信息,依靠的都是拉文章,現在我們也可以使用“推”的方式來訂閱。參看:PubSubHubbub

 

參考鏈接:

http://msdn.microsoft.com/zh-cn/library/dd783449(v=vs.100).aspx

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM