2013年6月27日 星期四

設計模式:觀察者模式 (發佈/訂閱模式) (Observer Pattern)

觀察者模式 (發佈/訂閱模式) (Observer Pattern),以下程式碼以 C# 為例

說明:
兩種類型的物件,「通知者」和「觀察者」。
訂閱:「通知者」可增減訂閱列表中的「觀察者」
發佈:當有監聽的事件發生時,「通知者」可從訂閱列表中,將事件通知「觀察者」,「觀察者」則會對此事件做相對應的動作。

功用:解除耦合,讓耦合的雙方依賴抽像(接口),而不依賴具體。


範例:
人們看到新聞事件的反應。(屬於酸民類別的只會說"酸",路人類別則只會說"讚")


希望達成如下的效果
static void Main(string[] args)
{
    ConcreteSubject s = new ConcreteSubject();

    s.Attach(new ConcreteObserverA(s, "酸民甲"));
    s.Attach(new ConcreteObserverA(s, "酸民乙"));
    s.Attach(new ConcreteObserverB(s, "路人丙"));
    s.Attach(new ConcreteObserverB(s, "路人丁"));

    s.SubjectState = "ABC";
    s.Notify();

    Console.WriteLine("-------------------");

    s.SubjectState = "DEF";
    s.Notify();

    Console.Read();
}
執行結果: 
ABC事件,酸民甲 說:酸酸酸~~
ABC事件,酸民乙 說:酸酸酸~~
ABC事件,路人丙 說:讚讚讚~~
ABC事件,路人丁 說:讚讚讚~~
-------------------
DEF事件,酸民甲 說:酸酸酸~~
DEF事件,酸民乙 說:酸酸酸~~
DEF事件,路人丙 說:讚讚讚~~
DEF事件,路人丁 說:讚讚讚~~



實現重點在於,「觀察者」要實做同一個方法 Update(),如此事件發生時,「通知者」才知道訂閱列表中的「觀察者」都執行 Update()。
(備註:C# 如果用 delegate 委派的方式來做,Update方法名稱就可以也不一樣了)

其餘程式碼
//通知者的抽象類別
abstract class Subject
{
    private IList<Observer> observers = new List<Observer>();

    //增加觀察者
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }
    //刪除觀察者
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }
    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
        {
            o.Update();
        }
    }
}
//通知者實做範例
class ConcreteSubject : Subject
{
    private string subjectState;

    //具體通知者狀態
    public string SubjectState
    {
        get { return subjectState; }
        set { subjectState = value; }
    }
}

//觀察者的抽象類別
abstract class Observer
{
    public abstract void Update();
}

//第一種觀察者實做範例
class ConcreteObserverA : Observer
{
    private string name;
    private ConcreteSubject subject;
    public ConcreteObserverA(ConcreteSubject subject, string name)
    {
        this.subject = subject;
        this.name = name;
    }

    public override void Update()
    {
        Console.WriteLine("{0}事件,{1} 說:酸酸酸~~",
          this.subject.SubjectState, name);
    }
}

//第二種觀察者實做範例
class ConcreteObserverB : Observer
{
    private string name;
    private ConcreteSubject subject;
    public ConcreteObserverB(ConcreteSubject subject, string name)
    {
        this.subject = subject;
        this.name = name;
    }

    public override void Update()
    {
        Console.WriteLine("{0}事件,{1} 說:讚讚讚~~",
          this.subject.SubjectState, name);
    }
}

相關連結:設計模式整理列表

沒有留言:

張貼留言