本章比較三種解耦模式——ABSTRACT SERVER、ADAPTER 與 BRIDGE——它們都用於在客戶端與伺服端之間建立間接層,但適用場景與設計取捨各不相同。
ABSTRACT SERVER 模式#
- 最簡單的解耦方式:在客戶端與伺服端之間放置一個介面
- 客戶端依賴介面而非具體類別,伺服端實作介面
- 這就是 DIP 的直接應用——依賴反轉,讓依賴方向指向抽象
Switch 與 Light 的例子#

Figure 33.2: A bad way to extend Switch
- 如果
Switch直接依賴Light,那麼要讓 Switch 控制其他設備(如 Motor)就必須修改 Switch——違反 OCP - 正確做法:定義
Switchable介面,Light與Motor都實作它,Switch只依賴Switchable
補充: ABSTRACT SERVER 介面應歸屬於客戶端而非伺服端——這是 DIP 的關鍵。介面的命名與設計應反映客戶端的需求,而非伺服端的能力。
ADAPTER 模式#
- 當伺服端的介面已經存在且無法修改時,ABSTRACT SERVER 模式不適用
- ADAPTER 在已有的類別之上包裝一層,使其符合客戶端期望的介面
兩種形式#

Figure 33.4: Solving the table lamp problem with ADAPTER

Figure 33.5: Solving the table lamp problem with ADAPTER
- 物件形式 ADAPTER:Adapter 持有被適配物件的參考,將呼叫轉發
- 類別形式 ADAPTER:Adapter 同時繼承客戶端介面與伺服端類別(在 C# 中透過介面實作加上繼承)
// 物件形式
public class LightAdapter : Switchable
{
private Light light;
public LightAdapter(Light light) { this.light = light; }
public void TurnOn() { light.TurnOn(); }
public void TurnOff() { light.TurnOff(); }
}Modem 問題#

Figure 33.7: Ideal solution to the modem problem
- 系統有一個
Modem介面(Dial,Hangup,Send,Receive),但有一種「專線 Modem」不需要撥號與掛斷 - 如果讓專線 Modem 實作
Dial和Hangup為空操作,就違反了 LSP

Figure 33.9: Solving the modem problem with ADAPTER
- 解法一:使用 ADAPTER,建立
DedicatedModemAdapter實作Modem介面 - 解法二:將
Modem介面拆成兩個——Connection(Dial/Hangup)與DataChannel(Send/Receive)

Figure 33.10: Solving the modem problem by merging type hierarchies
技巧: 選擇 ADAPTER 還是拆分介面,取決於實際情境。如果只有一兩個特例,ADAPTER 較為簡單;如果特例很多,重新設計介面階層可能更合適。
BRIDGE 模式#

Figure 33.11: BRIDGE solution to the modem problem
- BRIDGE 用於處理兩個獨立變化維度的問題
- 在 Modem 的例子中:一個維度是 Modem 的連線類型(撥號/專線),另一個維度是 Modem 的硬體實作(Hayes, USR, Ernie 等)
- 如果不使用 BRIDGE,類別數量會爆炸式增長:
DedicatedHayesModem,DialupHayesModem,DedicatedUSRModem, … - BRIDGE 將兩個維度分離成獨立的繼承階層,透過組合(而非繼承)連接:
- 一側是
Modem(連線類型的抽象) - 另一側是
ModemImplementation(硬體實作的抽象) - 具體的連線類型持有具體的硬體實作參考
- 一側是
注意: BRIDGE 模式增加了相當的複雜度。只有在確實存在兩個獨立變化維度時才應使用它。如果只有一個維度在變化,ADAPTER 或 ABSTRACT SERVER 就足夠了。
本章小結#
- ABSTRACT SERVER:最簡單的解耦——加一個介面。適用於你可以控制介面設計的情境
- ADAPTER:在已有類別之上包裝介面。適用於無法修改伺服端的情境
- BRIDGE:分離兩個獨立變化的維度。適用於型別組合會爆炸式增長的情境
三種模式的複雜度遞增,應從最簡單的開始考慮,只在需要時才升級到更複雜的方案。