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

.NET 設計模式深度解析:觀察者模式(Observer Pattern)完整實作指南

     編輯:關於.NET

什麼是觀察者模式?

觀察者模式定義了物件之間的一種一對多依賴關係,當一個物件(主題/Subject)的狀態發生改變時,所有依賴於它的物件(觀察者/Observer)都會得到通知並自動更新。這是 GoF《設計模式》中經典的行為型模式之一,廣泛應用於事件驅動系統、訊息通知機制以及 MVC 架構中的 View 更新。

生活中的直觀例子:拍賣會

想像一場拍賣會:拍賣師是主題,競標者是觀察者。每當有新出價(狀態變更),拍賣師會廣播給所有競標者,他們據此決定是否跟進。這完美詮釋了「一對多、鬆耦合、自動通知」的核心精神。

拍賣會觀察者模式示意圖

從問題導向重構:一步步演進到觀察者模式

初始設計的缺陷:強耦合

假設我們要監控微軟股價變化並通知投資者。初版代碼直接在 Microsoft 類別中持有 Investor 實例:

public class Microsoft {
    private Investor _investor;
    public void Update() { _investor.SendData(this); }
    // ... 屬性省略
}
public class Investor {
    public void SendData(Microsoft ms) { 
        Console.WriteLine("Notified {0} of {1}'s change to {2:C}", _name, ms.Symbol, ms.Price); 
    }
}

問題:Microsoft 與 Investor 雙向依賴,新增通知對象(如手機簡訊 Mobile)必須修改 Microsoft 類別,嚴重違反開放-封閉原則。

第一步:引入介面抽象觀察者

定義 IObserver 介面,讓 Microsoft 依賴抽象而非具體類別:

public interface IObserver { void SendData(Microsoft ms); }
public class Investor : IObserver { /* 實作 */ }
public class Microsoft {
    private IObserver _investor;
    public void Update() { _investor.SendData(this); }
    // ...
}

雖然降低了耦合,但仍只能支援單一觀察者,新增類型仍需修改主題類別。

第二步:主題維護觀察者集合

在 Microsoft 中改用 List<IObserver>,提供註冊/移除方法:

public class Microsoft {
    private List<IObserver> _observers = new List<IObserver>();
    public void AddObserver(IObserver o) { _observers.Add(o); }
    public void RemoveObserver(IObserver o) { _observers.Remove(o); }
    public void Update() { 
        foreach (var ob in _observers) ob.SendData(this); 
    }
}

現在支援多觀察者,且主題不再依賴具體觀察者類別。

第三步:抽象主題類別,支援多種股票

將共同邏輯上移至抽象基類 Stock,具體股票(Microsoft、IBM 等)繼承即可:

public abstract class Stock {
    private List<IObserver> _observers = new List<IObserver>();
    protected Stock(string symbol, double price) { Symbol = symbol; Price = price; }
    public void AddObserver(IObserver o) { _observers.Add(o); }
    public void RemoveObserver(IObserver o) { _observers.Remove(o); }
    public void Update() { 
        foreach (var ob in _observers) ob.SendData(this); 
    }
    public string Symbol { get; protected set; }
    public double Price { get; protected set; }
}
public class Microsoft : Stock {
    public Microsoft(string symbol, double price) : base(symbol, price) { }
}
public interface IObserver { void SendData(Stock stock); }
public class Investor : IObserver { /* 實作 */ }

此時已具備完整觀察者模式結構:Stock 是 Subject,IObserver 是 Observer 介面,具體觀察者實作介面,主題透過集合管理觀察者並統一通知。

觀察者模式最終 UML 結構圖

推模式 vs 拉模式

推模式

主題主動將資料作為參數「推」給觀察者(如上例 ob.SendData(this))。優點:觀察者被動接收,與主題鬆耦合。缺點:所有觀察者收到相同資料,無法按需取用;參數變動會影響所有觀察者。

拉模式

通知時不帶參數(ob.SendData()),觀察者自行向主題「拉」取所需資料。需讓觀察者持有主題參考:

public interface IObserver { void SendData(); }
public class Investor : IObserver {
    private Stock _stock;
    public Investor(string name, Stock stock) { _name = name; _stock = stock; }
    public void SendData() { 
        Console.WriteLine("Notified {0} of {1}'s change to {2:C}", _name, _stock.Symbol, _stock.Price); 
    }
}

優點:觀察者可按需取用資料。缺點:觀察者與主題耦合度提高,可透過抽象主題介面緩解。

.NET 原生實作:事件與委派

.NET 內建的事件/委派機制本質就是觀察者模式的語言級實現,更簡潔優雅:

public delegate void NotifyEventHandler(object sender);
public class Stock {
    public event NotifyEventHandler NotifyEvent;
    public void Update() { OnNotifyChange(); }
    protected virtual void OnNotifyChange() { NotifyEvent?.Invoke(this); }
    // ... Symbol, Price 屬性
}
public class Investor {
    public void SendData(object obj) {
        if (obj is Stock stock)
            Console.WriteLine("Notified {0} of {1}'s change to {2:C}", _name, stock.Symbol, stock.Price);
    }
}
// 使用
var stock = new Stock("Microsoft", 120.00);
var investor = new Investor("Jom");
stock.NotifyEvent += investor.SendData;
stock.Update();

委派扮演 Observer 介面,事件扮演 Subject 的註冊/通知機制,完全符合開放-封閉原則,且語法原生支援多播。

核心優勢與適用場景

  • 鬆耦合:主題與觀察者僅透過抽象依賴,可獨立變化、復用。
  • 自動傳播:主題發送通知時無需指定觀察者,觀察者自行決定是否訂閱。
  • 符合開放-封閉原則:新增觀察者無需修改主題代碼。

適用性:

  1. 一個抽象模型有兩個層面,其中一層依賴另一層,封裝為獨立物件以便獨立變化。
  2. 對一個物件的改變需要同時改變其他物件,但不知道具體有多少物件需改變。
  3. 一個物件必須通知其他物件,又不能假定它們是誰(避免緊密耦合)。

總結

觀察者模式透過「主題-觀察者」間接依賴取代直接耦合,極大提升系統可維護性與擴展性。在 .NET 中,優先考慮使用事件/委派原生實現;若需跨語言、跨進程或更精細控制通知邏輯,則採用經典介面ベース實作。掌握推/拉兩種模式權衡,能根據業務場景做出最佳設計決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved