這篇我們介紹 observer 模式。本篇會涵蓋:
GoF 如此描述 observer 模式:
定義物件間的一種一對多的相依關係,當一個物件的狀態發生改變時,所有相依於它的物件都將得到通知自動更新。
這個模式是當某事件發生時,某些物件同時需要得到通知,而這通知是希望自動發出。這模式能將通知者和被通知者解耦。
這個模式很常被使用到,又被稱為相依(Dependents)或發佈—訂閱(Publish-Subscribe)。
接下來,我們提一個案例來研究。我們一樣來看 Day10 中電子商務的例子。
我們希望這個系統在消費者進入系統時,做以下兩個操作:
首先我們應該都同意我們會先製作一個消費者的物件(Customer
),接下來就思考我們如何解決上述的問題。
其中一種解決方案是直接把以上兩個程序 hard-coded 進到 Customer
物件。但是這樣很顯然地會使程式非常不靈活且僵硬,上述的行為與 Customer
的耦合性也高,一但有了新的需求或是一些原因要移除上述的需求(根據時代演進,商業模型有所變化),維護這段程式碼會十分痛苦。
基於上述慘痛的方案,我們來看看 observer 模式是否能有更好的解法。在本案例中,我們實踐 observer 模式有四步驟:
第一步驟是我們要找出所有希望接收到通知的物件,他們被稱作 Observer;而本例中, Customer
物件被稱作 Subject。
而且我們希望所有 Observers 是相同的類型(共享同一個介面)。
第二步驟是:我們需要有一種方式使 Observers 知道自己要觀察哪些 Subject,因此我們要有一個註冊方式可以讓它們註冊自己。我們將在 Subject 中新增兩個 methods:
attach(Observer)
:將指定的 Observer 增加到 Subject 的觀察者清單中detach(Observer)
:從 Subject 的觀察者清單中刪除指定的 Observer事件發生時,Subject 會通知已經註冊的 Observer 們。因此我們需要為每個 Observer 實作一個 update()
method。
Subject 將實作一個 notify()
來 run through 所有已註冊的 Observer,並且呼叫每個 Observer 物件的 update()
。
除了通知 Observer 之外,我們也可能需要給予該事件的更多資訊。我們需要在 Subject 實作一些 method 來給其他 Observer 可能需要的資訊,再可以透過 update()
傳遞給 Observer。
以下是 UML 圖:
我們將用 Java
實作以上步驟:
import java.util.*;
class Customer {
static private Vector myObservers;
static {
myObservers = new Vector();
}
public static void attach(Observer o) {
// 註冊
myObservers.addElement(o);
}
public static void detach(Observer o) {
myObservers.remove(o);
}
public String getState() {
// 以一些方式取得資訊,在此實作先回傳 null
return null;
}
public void notify() {
// 進行一些設定,讓 observers 知道發生什麼事
for(Enumeration e = myObservers.elements();
e.hasMorElements(); ) {
((Observer) e).update(this);
}
}
}
interface Observer {
void update(Customer myCust);
}
class AddrVerification implements Observer {
public AddrVerification() {
}
public void update(Customer myCust) {
// 透過 myCust 驗證地址
// 取得相關客戶更多訊息
}
}
class WelcomeLetter implements Observer {
public WelcomeLetter() {
}
public void update(Customer myCust) {
// 處理寄歡迎信的操作
// 透過 myCust 取得資訊
}
}
以下是本模式的關鍵特徵:
項目 | 內容 |
---|---|
意圖 | 在物件之間定義一種一對多的相依關係,如此當一個物件的狀態改變時,所有希鴦者都將得到通知並自動更新 |
問題 | 當某個事件發生時,需要像一系列變化著的物件發出通知 |
解決方案 | Observer 將監視某個事件的責任委託給 Subject |
參與者與協作者 | Subject 知道自己的 Observer ,因為 Observer 要向它註冊。Subject 必須在所監視的始建發生時通知 Observer 。Observer 負責向 Subject 註冊,以及在得到通知時從 Subject 處獲取資訊 |
效果 | 如果某些 Observer 只對事件的一個子集感興趣,那麼 Subject 可能會告訴它們不需要知道的事件。如果 Subject 通知 Observer ,Observer 還返回請求更多資訊,則可能需要額外的通訊 |
實作 | 讓某個事件發生需要知道的物件將自己註冊到另一個監視事件發生或自己觸發事件的物件上。 |
以下是本模式的 UML 圖:
回頭看剛剛的例子,如果 WelcomeLetter
與 AddrVerification
是已經存在的物件,那麼我們就會使用 adapter 模式讓它放進同一個共用介面中。
一個 Observer 可能只需要處理事件的某些情況。在這種情況下我們可能需要將額外的通知篩選掉。
將篩選通知到責任轉給 Subject
物件,可以避免額外的通知。我們可以在此讓 Subject
物件使用 strategy 模式決定通知是否該發出。每個 Observer 註冊時都將正確的 Strategy
物件提供給 Subject
物件。
下篇我們會提到 Template Method 模式。ㄅㄅ!