本章主軸#

觀察者模式(Observer)解決一個常見問題:當某事件發生時,要通知一群「現在不確定有多少」的物件。它是「解耦」這件事最具代表性的模式。

模式分類略談#

GoF 把模式分為三類:

類別用途例子
Creational(生成)建立物件Abstract Factory、Singleton、Factory Method
Structural(結構)串接既有物件Facade、Adapter、Bridge、Decorator
Behavioral(行為)容納可變的行為Strategy、Observer

作者額外提出第四類:解耦類(decoupling)模式——目的是降低物件耦合,提升擴充性。Observer 是其中代表。

案例:新增客戶後要做什麼?#

新需求:每次新增客戶要:

  • 寄歡迎信
  • 用郵局服務驗證地址

直接寫死的做法#

Figure 18-1: 把通知行為寫死在 Customer 裡——加新需求就要改 Customer

把通知對象都寫進 Customer.add() 裡。問題:

  • 需求一定會再變
  • 加新動作要改 Customer
  • 不同公司可能要不同的歡迎信,需多版 Customer

GoF 對 Observer 的意圖描述#

在物件之間定義一對多的依賴關係;當某個物件改變狀態時,所有依賴者都會自動被通知並更新。

套到案例上的四步驟#

  1. 讓 Observer 共用同一個介面——Subject 才能用一致方式通知
  2. 由 Observer 自行向 Subject 註冊——attach(Observer) / detach(Observer)
  3. 事件發生時逐一呼叫 update——Subject 走過清單通知大家
  4. 提供查詢方法——Observer 可從 Subject 取得事件相關資訊
public class Customer {
    static private Vector myObs = new Vector();
    public static void attach(MyObserver o) { myObs.addElement(o); }
    public static void detach(MyObserver o) { myObs.remove(o); }

    public void notifyObs() {
        for (Enumeration e = myObs.elements(); e.hasMoreElements();) {
            ((MyObserver) e.nextElement()).update(this);
        }
    }
}

interface MyObserver { void update(Customer c); }

Figure 18-2: 用 Observer 實作 Customer——WelcomeLetter 與 AddrVerification 自行向 Customer 註冊

加新需求很輕鬆#

新需求:對居住在實體店面 20 哩內的新客戶寄折扣券。

  • 新增 BrickAndMortar 觀察者,邏輯內自行過濾距離
  • 不必動 Customer 任何一行

既有類別可能無法修改?用 Adapter 包成符合 Observer 介面的 wrapper。

Observer 的關鍵特性#

欄位內容
Intent一對多依賴;狀態改變時所有依賴者自動更新
Problem要通知一個會變動的物件清單
SolutionObserver 主動註冊到 Subject;Subject 不必預知對象
Consequences可能傳了 Observer 不需要的事件;可能要回查 Subject 取得資訊
ImplementationObserver 註冊;事件發生時 Subject 呼叫 update;常與 Adapter 搭配

Figure 18-5: Observer 模式的通用結構——Subject 持有一群 Observer,狀態改變時自動通知

Java 內建支援#

java.util.Observable + Observer 介面內建實作。方法名為 addObserver / deleteObserver / notifyObservers

實務筆記#

不是每個依賴都該用 Observer#

票務系統中「品項變動 → 重算稅」這種固定且已知的依賴,不適合用 Observer。它會徒增複雜度。

當依賴清單會變、是動態決定、依環境而異時,Observer 才有真正價值。

過濾不必要的通知#

可在 Subject 端用 Strategy 模式做過濾——每個 Observer 註冊時把自己的判斷策略一起傳入。

不同 Observer 要不同資訊#

同樣可用 Strategy:Observer 提供它要 Subject 怎麼回報的策略。

模式中的 OO 原則#

  • 物件對自己負責:每個 Observer 自行抓資料、自行決定動作
  • 抽象類別:Observer 介面表達「需被通知」的概念
  • 多型封裝:Subject 不知具體 Observer 型別

本章重點#

  • Observer 是把「不可預知的依賴方」這個變動軸封裝起來
  • 讓事件來源不必預知所有可能的接收者
  • 多種變化可組合:Adapter、Strategy 都常與 Observer 搭配
  • 但別誤用——固定依賴不該硬塞 Observer