本章主軸#
橋接模式(Bridge)是本書到目前為止最複雜、也是最強大的模式。本章不只介紹 Bridge,還示範如何從問題中「推導」出這個模式——這個推導過程比結果更值得學。
GoF 對 Bridge 的意圖描述#
把抽象(abstraction)與其實作(implementation)解耦,使兩者可以獨立變化。
這段話容易讓人困惑。關鍵釐清:「實作」指的是抽象類別與其衍生類別所使用的物件——不是抽象類別的衍生類別本身(後者叫具象類別)。
推導範例:要畫多種形狀,又要支援多套繪圖程式#
初始需求#
- 必須支援 DP1、DP2 兩個繪圖程式
- 介面不一致:
- DP1:
draw_a_line(x1,y1,x2,y2)/draw_a_circle(x,y,r) - DP2:
drawline(x1,x2,y1,y2)/drawcircle(x,y,r)
- DP1:
- 起初只有 Rectangle,client 不應在意實際使用哪一個 DP
嘗試一:純繼承#
Rectangle為抽象類別,V1Rectangle、V2Rectangle各自實作drawLine- 後來追加 Circle,再多一層
Shape抽象,Rectangle與Circle都繼承自它 - 每種形狀都得衍生兩個版本(V1、V2)

Figure 10-3: 直觀做法——兩種 Shape × 兩種繪圖程式造成的類別爆炸

Figure 10-7: 替代繼承結構——先依繪圖程式分支,仍無法消除重複
問題浮現:類別爆炸#
- 形狀 × 繪圖程式 = 類別數
- 加一個新繪圖程式 → 多 N 個類別
- 加一個新形狀 → 多 M 個類別
- 還伴隨重複程式碼、低凝聚、緊密耦合
這正是「過度依賴繼承」的典型症狀。Alexander 的話就是:抽象(形狀)與實作(繪圖程式)被綁死了,無法獨立變化。
在不知道解法前就能識別問題#
學習設計模式時的常見錯誤:盯著「解法」找適用之處。
更有效的做法是研究模式背後的問題——這樣才知道「何時、為什麼」要用它。
「我有一個抽象,它有不同實作;我希望這兩個維度能獨立變動。」——一旦你能描述問題,即使還不會實作 Bridge,也已經知道它就是合適的解法。
用基本策略推導 Bridge#
依循兩條老原則:
- 找出變動點並封裝
- 偏好聚合勝於繼承
步驟一:分別封裝兩個變動軸#
- 把形狀的變化(Rectangle、Circle、…)封進
Shape - 把繪圖程式的變化(V1Drawing、V2Drawing、…)封進
Drawing

Figure 10-9: 找出變動點——抽象的 Shape 與 Drawing

Figure 10-10: 表達變化——Shape 衍生 Rectangle/Circle,Drawing 衍生 V1Drawing/V2Drawing
步驟二:決定誰使用誰#
Drawing不該知道 Shape 的細節(會違反「物件對自己負責」與封裝)- 應由
Shape使用Drawing
完成設計#
Shape持有Drawing參考,呼叫drawLine、drawCircleV1Drawing內呼叫 DP1,V2Drawing內呼叫 DP2- 任何時刻只有三個物件:Client、某個具體 Shape、某個具體 Drawing

Figure 10-11: 把兩個類別樹串起來——Shape 透過聚合使用 Drawing

Figure 10-12: 擴充設計——加入 drawLine、drawCircle 與 DP1/DP2 連結

Figure 10-13: 抽象與實作徹底分離——左為 Shape Abstraction,右為 Drawing Implementation

Figure 10-14: 任何時刻其實只有三個物件:Shape、Drawing、DP
abstract class Shape {
protected Drawing myDrawing;
abstract public void draw();
Shape(Drawing drawing) { myDrawing = drawing; }
protected void drawLine(double x1, double y1, double x2, double y2) {
myDrawing.drawLine(x1, y1, x2, y2);
}
protected void drawCircle(double x, double y, double r) {
myDrawing.drawCircle(x, y, r);
}
}「One rule, one place」——
Shape內保留drawLine、drawCircle方法,避免每個 Rectangle / Circle 子類都直接呼叫 Drawing。將來其他形狀也能重用,且改動只在一處。
Bridge 的關鍵特性#
| 欄位 | 內容 |
|---|---|
| Intent | 解耦一群實作與使用它們的物件 |
| Problem | 抽象的衍生類別需使用多種實作,又不能讓類別數爆炸 |
| Solution | 為實作定義介面,由抽象類別的衍生類別透過該介面使用 |
| Participants | Abstraction 定義對外的介面;Implementor 定義實作介面;Concrete 雙方互不知對方實際型別 |
| Consequences | 提升擴充性;client 不知實作細節;類別數隨「形狀數 + 實作數」線性增長 |
| Implementation | 把實作封進抽象類別或介面,Abstraction 持有其參考 |

Figure 10-15: Bridge 模式的通用結構——Abstraction 持有 Implementor,雙方互不知具體型別
實務筆記#
印表機驅動是經典案例#
但 Bridge 真正威力在於提醒你:當你看到 X 永遠搭配 S、Y 永遠搭配 T 時,可以考慮把 S、T 抽象成共同實作介面,讓 X、Y 都能任選其一。
常與 Adapter 搭配使用#
兩個(或更多)模式緊密整合時,稱為複合模式(compound design pattern)——不再是「composite」是為避免和 Composite 模式混淆。
V1、V2 是現成系統,介面非作者所定,因此需要 Adapter 把它們適配成 Drawing 的介面。
Bridge 不總是完美#
- 加新實作很乾淨——只新增一個 ConcreteImplementor
- 加新抽象有時會逼你修改實作介面(例如要新增橢圓 →Drawing 要加新方法)
- 但即使要改,影響範圍仍局限、有明確流程
設計模式不是給你完美解法,而是凝聚多年集體經驗的「比你獨自想出來更好」的解法。
從重構(refactoring)的角度看 Bridge#
- 不要為了未來「可能」的多實作就先做 Bridge
- 透過遵守「One rule, one place」,將來真的需要 Bridge 時也能輕鬆重構
Bridge 中的物件導向原則#
| 原則 | 在 Bridge 裡的展現 |
|---|---|
| 物件對自己負責 | 各種 Shape 自行呼叫 draw |
| 抽象類別 | Shape、Drawing 都是純概念佔位符 |
| 透過抽象類別封裝 | Client 看不到具體 Shape;Shape 看不到具體 Drawing |
| One rule, one place | drawLine、drawCircle 集中在 Shape |
| 可測試性 | N×M 種組合的測試,被縮減成 N+M 個獨立測試 |
本章要記住的事#
- Bridge 的本質:抽象與實作分屬兩條獨立繼承樹,透過聚合連結
- 它解決多軸變動下的類別爆炸問題
- 你可以在「會不會實作」之前,就先「決定要用 Bridge」
- 常與 Adapter 搭配;最終的解法可能是複合模式
- 模式的價值在於提升思考層次,幫你看見原本被細節遮蔽的可能