首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 設計模式精講:觀察者模式

     編輯:關於C#

一、觀察者模式:一對多依賴的自動通知機制

又稱發布-訂閱、模型-視圖、源-監聽器模式。定義一對多依賴:多個觀察者同時監聽一個主題,主題狀態變化時自動通知所有觀察者更新。為實現低耦合下的高協作,觀察者模式是關鍵方案。

二、標準結構與四大角色

觀察者模式類圖

  1. 抽象主題:管理觀察者引用集合,提供附加/移除/通知介面。
  2. 抽象觀察者:定義更新介面(Update())。
  3. 具體主題:存儲狀態,狀態變更時調用 Notify()。
  4. 具體觀察者:實現更新介面,保持與主題狀態自恰,可持有主題引用。

主題面向抽象觀察者編程,動態維護觀察者列表,實現鬆耦合。

三、傳統實現示例


// Observer pattern -- Structural example
using System; using System.Collections;

abstract class Subject {
    private ArrayList observers = new ArrayList();
    public void Attach(Observer o) { observers.Add(o); }
    public void Detach(Observer o) { observers.Remove(o); }
    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 { abstract public void Update(); }

class ConcreteObserver : Observer {
    private string name, observerState; private ConcreteSubject subject;
    public ConcreteObserver(ConcreteSubject s, string n) { subject = s; name = n; }
    public override void Update() {
        observerState = subject.SubjectState;
        Console.WriteLine("Observer {0}'s new state is {1}", name, observerState);
    }
    public ConcreteSubject Subject { get { return subject; } set { subject = value; } }
}

public class Client {
    public static void Main() {
        ConcreteSubject s = new ConcreteSubject();
        s.Attach(new ConcreteObserver(s, "1"));
        s.Attach(new ConcreteObserver(s, "2"));
        s.Attach(new ConcreteObserver(s, "3"));
        s.SubjectState = "ABC"; s.Notify();
    }
}

四、C# 委託與事件簡化實現

.NET 原生委託/事件機制可大幅簡化觀察者模式:


// Observer pattern -- Structural example (Delegate/Event)
using System;

delegate void UpdateDelegate();

class Subject {
    public event UpdateDelegate UpdateHandler;
    public void Attach(UpdateDelegate ud) { UpdateHandler += ud; }
    public void Detach(UpdateDelegate ud) { UpdateHandler -= ud; }
    public void Notify() { if (UpdateHandler != null) UpdateHandler(); }
}

class ConcreteSubject : Subject {
    private string subjectState;
    public string SubjectState { get { return subjectState; } set { subjectState = value; } }
}

class ConcreteObserver {
    private string name, observerState; private ConcreteSubject subject;
    public ConcreteObserver(ConcreteSubject s, string n) { subject = s; name = n; }
    public void Update() {
        observerState = subject.SubjectState;
        Console.WriteLine("Observer {0}'s new state is {1}", name, observerState);
    }
    public ConcreteSubject Subject { get { return subject; } set { subject = value; } }
}

class AnotherObserver {
    public void Show() { Console.WriteLine("AnotherObserver got an Notification!"); }
}

public class Client {
    public static void Main() {
        ConcreteSubject s = new ConcreteSubject();
        ConcreteObserver o1 = new ConcreteObserver(s, "1"), o2 = new ConcreteObserver(s, "2");
        AnotherObserver o3 = new AnotherObserver();
        s.Attach(new UpdateDelegate(o1.Update));
        s.Attach(new UpdateDelegate(o2.Update));
        s.Attach(new UpdateDelegate(o3.Show)); // 任意符合簽名的方法均可訂閱
        s.SubjectState = "ABC"; s.Notify();
        Console.WriteLine("--------------------------");
        s.Detach(new UpdateDelegate(o1.Update));
        s.SubjectState = "DEF"; s.Notify();
    }
}

關鍵點: 委託規範方法簽名;事件封裝委託多播;+=/-= 訂閱/退訂;Invoke() 廣播通知。

五、實戰案例:股價變動通知投資者


// Observer pattern -- Real World example
using System; using System.Collections;

abstract class Stock {
    protected string symbol; protected double price;
    private ArrayList investors = new ArrayList();
    public Stock(string symbol, double price) { this.symbol = symbol; this.price = price; }
    public void Attach(Investor i) { investors.Add(i); }
    public void Detach(Investor i) { investors.Remove(i); }
    public void Notify() { foreach (Investor i in investors) i.Update(this); }
    public double Price {
        get { return price; }
        set { price = value; Notify(); } // 價格變更自動通知
    }
    public string Symbol { get { return symbol; } set { symbol = value; } }
}

class IBM : Stock { public IBM(string s, double p) : base(s, p) {} }

interface IInvestor { void Update(Stock stock); }

class Investor : IInvestor {
    private string name; private Stock stock;
    public Investor(string n) { name = n; }
    public void Update(Stock s) {
        Console.WriteLine("Notified investor {0} of {1}'s change to {2:C}", name, s.Symbol, s.Price);
    }
    public Stock Stock { get { return stock; } set { stock = value; } }
}

public class ObserverApp {
    public static void Main() {
        Investor s = new Investor("Sorros"), b = new Investor("Berkshire");
        IBM ibm = new IBM("IBM", 120.00);
        ibm.Attach(s); ibm.Attach(b);
        ibm.Price = 120.10; ibm.Price = 121.00; ibm.Price = 120.50; ibm.Price = 120.75;
    }
}

六、優缺點總結

優點:

  1. 主題與觀察者抽象耦合,屬於不同抽象層次。
  2. 支持廣播通信,自動通知所有註冊觀察者。
  3. 表示層與數據邏輯層分離,介面清晰。

缺點:

  1. 觀察者過多時通知開銷大。
  2. 循環依賴導致遞歸調用崩潰,需特別防範。
  3. 異步投遞需保證自恰性。
  4. 觀察者知曉「變了」但不知「怎麼變的」。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved