在軟體建構過程中,常需建立「通知依賴關係」:目標物件狀態變更時,自動通知所有依賴的觀察者物件。若依賴過於緊密,系統難以應對變化。觀察者模式透過面向物件抽象,將此依賴弱化為穩定的鬆耦合關係,是實現發布-訂閱機制的經典模式。
意圖:定義物件間一對多依賴,當一個物件狀態改變,所有依賴物件自動獲得通知並更新。 (GoF《設計模式》)
結構圖:

拍賣師 (目標) 觀察投標人 (觀察者) 舉牌報價,每次新報價改變當前價格並廣播給所有投標人:

public class Microsoft { // 目標
private Investor _investor; // 直接依賴具體觀察者
public void Update() => _investor.SendData(this);
}
public class Investor { // 觀察者
public void SendData(Microsoft ms) => Console.WriteLine(...);
}
缺點:雙向依賴、新增觀察者類型 (如 Mobile) 需修改目標類別,違反開放-封閉原則。
public interface IObserver { void SendData(Microsoft ms); }
public class Investor : IObserver { ... }
public class Microsoft {
private IObserver _investor; // 依賴抽象
public void Update() => _investor.SendData(this);
}
進步:目標不再依賴具體觀察者,但仍只能單一觀察者,新增類型仍需修改目標。
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() => _observers.ForEach(o => o.SendData(this));
}
支援多觀察者,但觀察者仍依賴具體目標 Microsoft。
public abstract class Stock {
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() => _observers.ForEach(o => o.SendData(this));
public string Symbol { get; }
public double Price { get; }
}
public class Microsoft : Stock { ... }
public interface IObserver { void SendData(Stock stock); }
public class Investor : IObserver { ... }
達成:目標與觀察者雙向解耦,符合開放-封閉原則,系統可維護性與擴充性大幅提升。

| 模式 | 機制 | 優點 | 缺點 |
|---|---|---|---|
| 推模式 | 通知時帶參數傳遞完整資訊 | 觀察者被動接收,與目標鬆耦合 | 資訊可能冗餘;參數變更影響所有觀察者 |
| 拉模式 | 通知無參數,觀察者主動向目標取回所需資訊 | 按需取用,資訊精確 | 觀察者需持有目標參考,耦合稍強 |
文中提供完整拉模式程式碼範例 (略)。
C# 事件/委派是更優雅的原生實作:委派扮演抽象觀察者介面,事件提供者為目標物件,耦合度更低。
public delegate void NotifyEventHandler(object sender);
public class Stock {
public event NotifyEventHandler NotifyEvent;
public void Update() => OnNotifyChange();
protected virtual void OnNotifyChange() => NotifyEvent?.Invoke(this);
}
public class Investor {
public void SendData(object obj) { if (obj is Stock s) Console.WriteLine(...); }
}
// 用戶端
var stock = new Stock("Microsoft", 120);
stock.NotifyEvent += new Investor("Jom").SendData;
stock.Update();
觀察者模式將一對多通知依賴轉化為穩定的鬆耦合關係,大幅提升系統可維護性與擴充性,完美契合開放-封閉原則。.NET 開發者應優先考慮事件/委派原生實作。