意圖(Intent)#
將抽象與其實作分離,使兩者可以獨立變化。
別名(Also Known As)#
Handle/Body
動機(Motivation)#
考慮一個可攜式的 Window 抽象,需要同時支援 X Window System 和 IBM Presentation Manager。直覺做法是用繼承:定義抽象 Window 類別,再分別建立 XWindow 和 PMWindow 子類別。但這有兩個嚴重問題:
- 類別爆炸:每增加一種視窗類型(如 IconWindow),就得為每個平台各建一個子類別(XIconWindow、PMIconWindow)。支援第三個平台時,每種視窗類型都需要再加一個子類別
- 平台依賴:客戶端直接實例化具體類別(如 XWindow),使程式碼綁定在特定平台實作上
Bridge 模式的解法是將視窗的抽象層級與平台實作層級分成兩個獨立的繼承體系:
- 抽象層:Window、IconWindow、TransientWindow
- 實作層:WindowImp、XWindowImp、PMWindowImp
Window 的所有操作都透過 WindowImp 的抽象操作來實現,這就是連接兩個層級的「橋樑」。
Bridge 的核心原則:以組合取代多重繼承來處理正交的變化維度。當抽象和實作各自有多個變體時,將它們放在不同的類別體系中,透過組合連接,避免類別數量的乘法爆炸。
適用場景(Applicability)#
- 想避免抽象與實作之間的永久綁定,例如需要在執行期選擇或切換實作
- 抽象和實作都需要透過子類別化來擴展,且需獨立擴展
- 實作的變更不應影響客戶端程式碼(不需重新編譯)
- 出現「nested generalizations」—— 類別體系呈現巢狀泛化的結構,表示需要將物件拆成兩個部分
- 想讓多個物件共享同一個實作(可能使用 reference counting),但對客戶端隱藏此事實
結構(Structure)#
Abstraction 持有一個指向 Implementor 的引用。客戶端透過 Abstraction 的介面操作,Abstraction 將請求委派給 Implementor。兩個層級各自可以獨立擴展子類別。
classDiagram
class Abstraction {
+Operation()
}
class RefinedAbstraction {
+Operation()
}
class Implementor {
<<interface>>
+OperationImpl()
}
class ConcreteImplementorA {
+OperationImpl()
}
class ConcreteImplementorB {
+OperationImpl()
}
Abstraction <|-- RefinedAbstraction
Abstraction o--> Implementor
Implementor <|.. ConcreteImplementorA
Implementor <|.. ConcreteImplementorB參與者(Participants)#
| 參與者 | 範例 | 職責 |
|---|---|---|
| Abstraction | Window | 定義抽象介面,持有 Implementor 的引用 |
| RefinedAbstraction | IconWindow | 擴展 Abstraction 定義的介面 |
| Implementor | WindowImp | 定義實作類別的介面。不需要與 Abstraction 的介面完全對應——通常 Implementor 提供基本操作(primitive operations),Abstraction 基於這些基本操作定義更高層的操作 |
| ConcreteImplementor | XWindowImp、PMWindowImp | 實作 Implementor 介面 |
協作方式(Collaborations)#
- Abstraction 將客戶端請求轉發給其 Implementor 物件
優缺點(Consequences)#
- 介面與實作解耦:實作不再永久綁定於介面,可在執行期配置甚至切換。消除了編譯期的實作依賴,有助於建構更好的分層系統
- 提升可擴展性:Abstraction 和 Implementor 兩個層級可獨立擴展
- 隱藏實作細節:客戶端無需知道實作物件的共享機制或 reference counting 等細節
實作要點(Implementation)#
- 只有一個 Implementor 的退化情況:即使只有一個實作,分離仍有價值——修改實作時客戶端只需重新連結(relink),不需重新編譯。C++ 中稱為「Cheshire Cat」手法
- 建立正確的 Implementor:
- Abstraction 的建構式根據參數決定使用哪個 ConcreteImplementor
- 先使用預設實作,後續根據使用情況動態切換
- 委派給 Abstract Factory 來封裝平台相關的決策
- 共享 Implementor:利用 Handle/Body 慣用法搭配 reference counting 實現多物件共享同一實作
- 多重繼承的限制:C++ 可用多重繼承將介面與實作結合,但這是靜態綁定,無法實現真正的 Bridge
當有多個 ConcreteImplementor 可選擇時,使用 Abstract Factory 來建立 Implementor 是最佳做法。這讓 Abstraction 完全不依賴任何具體的 Implementor 類別。
已知應用(Known Uses)#
- ET++:Window/WindowPort 設計,WindowPort 有 XWindowPort、SunWindowPort 等子類別,透過 WindowSystem 抽象工廠建立
- libg++:Set(抽象)搭配 LinkedList、HashTable(實作),透過 LinkedSet、HashSet 進行橋接
- NeXT AppKit:NXImage/NXImageRep 橋接,NXImage 可以同時持有多個 NXImageRep 實作,根據顯示裝置選擇最佳的
相關模式(Related Patterns)#
- Abstract Factory:可用來建立和配置特定的 Bridge
- Adapter:用於事後讓不相容的類別合作。Bridge 則是在設計初期使用,讓抽象和實作從一開始就能獨立變化