學習設計模式時,總有一個讓人卡住的階段。
那就是工廠方法 vs 抽象工廠。兩者名稱相近,也都會「代替你建立物件」,很容易混淆。
這也是面試常見題目,所以我在本文一次整理好了。
先講重點。工廠方法是把物件要建立成哪個類別交給子類別決定;抽象工廠則會完整建立彼此相關的多個物件(產品族)。
像 static func make 這種替建立流程命名的靜態工廠,和 GoF 工廠方法是不同的概念。如果想了解實務上的 API 設計,建議先將它和 Swift 靜態工廠方法 區分開來。
一個,還是一整組,這就是最大的差異。
什麼是工廠方法?
工廠方法是將建立物件的責任,從上層類別交給子類別的模式。
上層類別只定義「建立按鈕」,至於要建立哪種按鈕則由子類別決定。
直接看程式碼會更快理解。
protocol Button { func render() }
class Dialog {
// 工廠方法:由子類別決定要建立什麼
func createButton() -> Button { fatalError("在子類別中實作") }
func render() {
let button = createButton()
button.render()
}
}
class IOSDialog: Dialog {
override func createButton() -> Button { IOSButton() }
}
Dialog 不需要知道自己使用哪種按鈕。新增平台時,只要再建立一個子類別即可。
核心在於繼承。這是透過子類別化替換建立位置的結構。
抽象工廠有什麼不同?
抽象工廠是將相關物件綁成一組來建立的模式。
它負責的不是單一按鈕,而是按鈕、核取方塊、捲軸等必須一起使用的物件。
protocol GUIFactory {
func createButton() -> Button
func createCheckbox() -> Checkbox
}
class IOSFactory: GUIFactory {
func createButton() -> Button { IOSButton() }
func createCheckbox() -> Checkbox { IOSCheckbox() }
}
class MacFactory: GUIFactory {
func createButton() -> Button { MacButton() }
func createCheckbox() -> Checkbox { MacCheckbox() }
}
重點是一致性。使用 iOS 工廠時,按鈕和核取方塊都會採用 iOS 樣式,不會發生 iOS 按鈕混入 Mac 核取方塊的問題。
抽象工廠通常會從外部注入工廠物件後使用。它不是靠繼承,而是靠組合運作。
工廠方法以繼承解決「要建立什麼」,
抽象工廠則以組合解決「要建立哪一組」。
一覽工廠方法與抽象工廠的差異
只用文字說明容易混淆,所以整理成表格。
| 分類 | 工廠方法 | 抽象工廠 |
|---|---|---|
| 目的 | 委派建立單一物件 | 建立相關物件族 |
| 基礎 | 繼承(子類別化) | 組合(組成) |
| 建立內容 | 一種產品 | 多種產品(產品族) |
| 擴充方式 | 新增子類別 | 新增工廠 |
| 常見範例 | createButton() | 完整 GUIFactory |
有趣的是,抽象工廠內部常常其實是由工廠方法組成。與其說兩者互相競爭,不如看成規模不同的模式。
何時使用,何時避免?
實務上的判斷標準其實比想像中簡單。
- 如果只會建立一種物件,工廠方法就足夠了。硬是建立工廠介面,反而是過度設計。
- 如果有兩種以上的物件必須一起運作,請考慮抽象工廠。
- 如果產品族未來看來不會增加,直接使用條件分支有時反而更好。
| 情境 | 判斷 |
|---|---|
| 要建立的產品只有一個 | 工廠方法 |
| 產品會綁成一組運作 | 抽象工廠 |
| 產品族經常增加 | 抽象工廠 |
| 只需 2~3 個分支 | 不使用模式,採單純分支 |
模式不是目的,而是工具。對不會擴充的程式碼預先加入抽象化,只會讓程式更難閱讀。
面試時會這樣問
Q. 請說明工廠方法與抽象工廠的差異。 A. 工廠方法是將單一物件的建立委派給子類別的繼承型模式。抽象工廠則是以組合為基礎,一致地建立相關物件族。核心差異在於建立一個物件,還是一整組物件。
Q. 抽象工廠中會使用工廠方法嗎? A. 會,而且很常見。抽象工廠的各個建立方法,往往會在內部以工廠方法的形式實作。兩者不是對立概念,而是處理範圍不同的模式。
現在只要看到這兩種模式的名稱,就能想起「一個,還是一整組」,就算掌握重點了。面試前再快速讀一次本文,應該會更有把握。

