本章主軸#
抽象工廠(Abstract Factory)用來協調一群相關物件的實例化。當系統需要根據情境一次拿到一整組搭配正確的物件時,就是它的舞台。本章用「不同效能機器要用不同解析度的驅動程式」為例,逐步推導出 Abstract Factory。
GoF 對 Abstract Factory 的意圖描述#
提供一個介面,用來建立一群相關或相依的物件,而不需指定它們的具象類別。
推導範例:依機器選擇驅動程式#
場景#
| 驅動類別 | 低效能機器 | 高效能機器 |
|---|---|---|
| Display 驅動 | LRDD(低解析顯示) | HRDD(高解析顯示) |
| Print 驅動 | LRPD(低解析列印) | HRPD(高解析列印) |
「家族(family)」按應用場景區分——例如低解析家族、高解析家族;中階機器也可能混搭。
嘗試一:直接用 switch#
在 ApControl 內以 switch 決定要 new 哪種驅動。問題:
- 緊密耦合:若新增「中階」這個解析度層級,要改多個 switch
- 弱凝聚:
doDraw與doPrint同時負責畫圖/列印與選驅動
Switch 通常暗示兩件事:
- 應該改用多型
- 責任放錯地方
嘗試二:以繼承特化 ApControl#

Figure 11-1: 以繼承處理變化——LowResApControl 與 HighResApControl 各自實例化對應驅動
每組(低效能、高效能、…)各做一個 ApControl 子類。問題依舊:
- 組合爆炸:每多一個變動軸就乘一倍
- 類別意義模糊
- 違反「偏好聚合勝於繼承」
嘗試三:抽象化 + 工廠物件#
- 把驅動抽象成
DisplayDriver、PrintDriver,ApControl只面向抽象操作

Figure 11-2: 將驅動程式抽象化——DisplayDriver 與 PrintDriver
- 引入
ResFactory來「提供當前該用的驅動」

Figure 11-3: 理想情況——ApControl 只面對抽象 DisplayDriver 與 PrintDriver

Figure 11-5: ResFactory 抽象類別與 LowResFact/HighResFact 兩個具象工廠
ApControl與ResFactory各司其職:使用 vs 建立- LRDD/HRDD 若不來自同一族系,就用 Adapter 包成相同抽象

Figure 11-6: 中間方案——ApControl 透過 ResFactory 取得驅動

Figure 11-7: 結合 Abstract Factory 與 Adapter——讓不同族系的驅動共享抽象
abstract class ResFactory {
abstract public DisplayDriver getDispDrvr();
abstract public PrintDriver getPrtDrvr();
}
class LowResFact extends ResFactory {
public DisplayDriver getDispDrvr() { return new LRDD(); }
public PrintDriver getPrtDrvr() { return new LRPD(); }
}
class HighResFact extends ResFactory {
public DisplayDriver getDispDrvr() { return new HRDD(); }
public PrintDriver getPrtDrvr() { return new HRPD(); }
}把
ResFactory抽象化、由不同的具象工廠負責不同家族——這就是 Abstract Factory。「Abstract」指的是被建立出來的東西本身是抽象,而非工廠類別自己。
為什麼叫 Abstract Factory?#
不是因為工廠類別本身是抽象的,而是因為所建立出來的物件是由抽象來指定——DisplayDriver、PrintDriver 都是抽象。怎麼實作工廠變化是另一回事。
三個關鍵問題的歸屬#
| 問題 | 原本解法 | Abstract Factory 解法 |
|---|---|---|
| 我目前是哪個 case? | ApControl 自己知 | 配置檔指定要建立哪個具象工廠 |
| 如何管理這些資訊? | ApControl 管 | 每個具象工廠自己知道要做什麼 |
| 建構邏輯放哪裡? | ApControl 裡 | 工廠物件裡 |
「不就又用回 switch 了嗎?」是的,但 switch 被孤立到工廠裡,不再與使用方耦合,影響面非常小。
Abstract Factory 的關鍵特性#
| 欄位 | 內容 |
|---|---|
| Intent | 為特定 client 或 case 提供整組物件 |
| Problem | 一群相關物件需被協調地實例化 |
| Solution | 把實例化規則從使用方抽出,集中到工廠物件 |
| Participants | AbstractFactory 定義「該建立哪些家族成員」;ConcreteFactory 各自實作 |
| Consequences | 把「哪個物件要用」與「如何用該物件」隔開 |
| Implementation | 抽象工廠類別 + 每個家族一個具象工廠(也可改用配置檔、資料表) |

Figure 11-8: Abstract Factory 模式的通用結構——AbstractFactory 建立 AbstractProductA/B 家族
實務筆記#
何時適用#
需要根據以下任一變動軸提供不同物件家族時:
- 不同作業系統(跨平台應用)
- 不同效能等級
- 不同版本
- 不同使用者特性
- 不同地區設定(locale-dependent 對話框、日期格式)
變體:用配置檔代替具象工廠#
家族數量太多時,不必每個家族都做一個具象工廠:
- 配置檔指定要 new 的類別名稱
- Java/C# 透過
Class.forName等機制動態實例化 - 資料表的每一列代表一個家族
Adapter 經常一起出現#
不同家族的成員不一定來自同一基底類別。LRDD、HRDD 常常各自獨立,這時就需要先用 Adapter 把它們適配成共同的抽象介面,Abstract Factory 才能順利運作。
取得正確工廠的方式#
通常透過配置檔讀取:依配置內容實例化對應的具象工廠。或由主系統決定後,再把工廠物件傳入子系統。
與 CAD/CAM 問題的關聯#
CAD/CAM 系統需要「全套 V1 features」或「全套 V2 features」。Abstract Factory 正好用來保證實際拿到的這群 feature 物件版本一致。
本章要記住的事#
- Abstract Factory 的關鍵:所建立的物件以抽象指定;族與族之間搭配一致
- 把「使用」與「建立」徹底解耦
- switch 不是壞事,位置擺對才不會傳染整個系統
- 配置檔 / 反射可以讓 Abstract Factory 動態化,避免具象工廠數量爆炸
- 它常與 Adapter 並肩工作