本章介紹 OBSERVER 模式——一種讓物件在狀態改變時自動通知所有依賴者的設計模式。作者從數位時鐘的例子出發,逐步演化出多種 Observer 的實作方式,最後展示 C# 中 delegate/event 的原生支援。

動機:數位時鐘問題#

Figure 32.1: Clock object

  • 需要一個 DigitalClock 顯示時間,時間來源是 TimeSource
  • 問題:TimeSource 不應該知道 DigitalClock 的存在——它可能有多種顯示器

Figure 32.2: Testing the DigitalClock

逐步演化#

使用 ClockDriver 解耦#

Figure 32.3: Getting the TimeSource to update the ClockDriver

  • 引入 ClockDriver 作為中間協調者:TimeSource 通知 ClockDriverClockDriver 更新 TimeSink(顯示器)
  • TimeSink 是一個介面,DigitalClock 實作它
  • TimeSource 也是一個介面,具體的時間提供者實作它

處理多個觀察者#

Figure 32.6: Handling multiple TimeSink objects

  • 當有多個 TimeSink 時,ClockDriver 需要維護一個觀察者列表
  • 每當 TimeSource 發出通知,ClockDriver 遍歷列表通知所有 TimeSink

將註冊機制內建到 Subject#

Figure 32.7: Moving registration and update into TimeSource

  • 觀察者的註冊(Register)與通知(Notify)邏輯可以移入 Subject 基底類別
  • 這就是典型的 Observer 模式Subject 維護觀察者列表,狀態改變時呼叫 Notify()

Pull Model vs. Push Model#

Figure 32.12: Canonical pull-model Observer

Pull Model(拉取模型)#

  • Observer 收到通知後,主動向 Subject 查詢所需的資料
  • 優點:Observer 只取用它需要的資料
  • 缺點:可能需要多次查詢,效率較低

Figure 32.13: Push-model Observer

Push Model(推送模型)#

  • Subject 在通知時主動將資料傳遞給 Observer
  • 優點:效率高,一次傳遞所有需要的資料
  • 缺點:Subject 需要知道 Observer 需要什麼資料,耦合度較高

C# 的 Delegate 與 Event#

Figure 32.8: Using multiple inheritance in C++ to separate Clock from

Figure 32.9: Observer delegation hack in C#

  • C# 提供了語言層級的 Observer 支援:delegateevent
  • delegate 是型別安全的函式指標,可以持有多個方法的參考(multicast)
  • event 是 delegate 的封裝,限制只有宣告者可以觸發
public delegate void TimeChangedHandler(int hours, int minutes, int seconds);

public class TimeSource
{
    public event TimeChangedHandler TimeChanged;

    protected void OnTimeChanged(int h, int m, int s)
    {
        TimeChanged?.Invoke(h, m, s);
    }
}

public class DigitalClock
{
    public void Register(TimeSource source)
    {
        source.TimeChanged += OnTimeChanged;
    }

    private void OnTimeChanged(int hours, int minutes, int seconds)
    {
        // 更新顯示
    }
}
sequenceDiagram
    participant TS as TimeSource
    participant DC as DigitalClock
    participant AC as AnalogClock

    Note over DC,AC: 註冊 event
    DC->>TS: TimeChanged += OnTimeChanged
    AC->>TS: TimeChanged += OnTimeChanged

    Note over TS: 時間改變
    TS->>DC: OnTimeChanged(h, m, s)
    TS->>AC: OnTimeChanged(h, m, s)

技巧: C# 的 delegate/event 機制讓 Observer 模式的實作極為簡潔——不需要定義 Observer 介面、不需要維護觀察者列表、不需要手動遍歷通知。語言層級的支援消除了大量樣板程式碼。

注意: 使用 event 時要注意記憶體洩漏問題——如果 Observer 註冊了 event 但忘記取消註冊(-=),即使 Observer 已不再使用,它仍會被 Subject 的 event 持有參考而無法被垃圾回收。

本章小結#

OBSERVER 模式讓 Subject 與 Observer 之間保持鬆散耦合——Subject 不需要知道 Observer 的具體型別,只需透過介面(或 delegate)通知即可。C# 的 delegate/event 語法讓這個模式的實作變得極為自然與簡潔。