意圖(Intent)#
定義一個建立物件的介面,但讓子類別決定要實體化哪一個類別。Factory Method 讓類別將實體化的動作延遲到子類別。
別名(Also Known As)#
Virtual Constructor
動機(Motivation)#
假設有一個支援多文件的應用程式框架,其中 Application 與 Document 都是抽象類別。要建立繪圖應用程式,需定義 DrawingApplication 與 DrawingDocument。Application 負責管理 Document,並在使用者選擇「開啟」或「新增」時建立它們。
問題在於:Application 只知道何時該建立 Document,卻不知道該建立哪一種 Document——因為具體的 Document 子類別是應用程式特有的。框架必須實體化類別,但它只認識抽象類別。
Factory Method 模式的解法是:讓 Application 子類別覆寫一個抽象的 CreateDocument 操作,回傳適當的 Document 子類別。我們稱 CreateDocument 為 factory method,因為它負責「製造」物件。
適用場景(Applicability)#
- 類別無法預知需要建立的物件類別
- 類別希望由子類別來指定建立的物件
- 類別將職責委派給多個 helper 子類別之一,且想將「哪個 helper」的知識集中管理
結構(Structure)#
Creator 宣告 factory method,回傳 Product 型別。ConcreteCreator 覆寫 factory method,回傳 ConcreteProduct 實例。Creator 可能提供預設實作,也可能宣告為抽象方法。
classDiagram
class Product {
<<interface>>
}
class ConcreteProduct
class Creator {
+FactoryMethod()*
+AnOperation()
}
class ConcreteCreator {
+FactoryMethod()
}
Product <|.. ConcreteProduct
Creator <|-- ConcreteCreator
ConcreteCreator ..> ConcreteProduct : creates
Creator ..> Product : uses參與者(Participants)#
| 參與者 | 範例 | 職責 |
|---|---|---|
| Product | Document | 定義 factory method 所建立物件的介面 |
| ConcreteProduct | MyDocument | 實作 Product 介面 |
| Creator | Application | 宣告 factory method,回傳 Product 型別;可能提供預設的 factory method 實作;可能呼叫 factory method 來建立 Product |
| ConcreteCreator | MyApplication | 覆寫 factory method,回傳 ConcreteProduct 實例 |
協作方式(Collaborations)#
- Creator 依賴子類別定義 factory method,以回傳適當的 ConcreteProduct 實例
優缺點(Consequences)#
- 消除對具體類別的綁定 —— 程式碼只處理 Product 介面,能與任何使用者定義的 ConcreteProduct 協作
- 潛在缺點:可能需要額外子類別化 —— Client 可能僅為了建立特定的 ConcreteProduct 就得繼承 Creator,增加了類別數量
- 為子類別提供 hook —— 在類別內用 factory method 建立物件永遠比直接建立更有彈性。子類別可覆寫 factory method 來提供擴展版本的物件
- 連接平行的類別階層 —— 當類別將部分職責委派給獨立的類別時,factory method 能定義兩個平行階層之間的對應關係(如 Figure 與 Manipulator)
Factory Method 的核心原則:「在獨立的操作中建立物件,讓子類別能覆寫建立方式」——這確保了框架的開放擴展性。
實作要點(Implementation)#
- 兩種主要變體
- Creator 為抽象類別,不提供 factory method 的預設實作——子類別必須實作
- Creator 為具體類別,提供預設實作——子類別可選擇覆寫
- 參數化 Factory Method —— 讓 factory method 接受參數來指定要建立的產品種類,支援多種產品。子類別可覆寫以改變或擴展支援的產品
- 語言特定的變體
- Smalltalk 中可回傳「要實體化的類別」,實現更晚的繫結
- C++ 中 factory method 通常是 virtual function,且不應在建構子中呼叫(因為子類別的 factory method 尚未可用)
- Lazy Initialization —— 避免在建構子中呼叫 factory method,改用存取器在首次需要時才建立產品
- 使用 template 避免子類別化 —— 在 C++ 中可用 template 子類別,以產品類別作為型別參數,免去手動繼承
採用命名慣例讓 factory method 更易辨識,例如 MacApp 框架使用
DoMakeClass()格式。清楚的命名能讓其他開發者一眼看出這是 factory method。
已知應用(Known Uses)#
- MacApp 與 ET++ 中的 Document 框架是典型應用
- Smalltalk-80 MVC 中 View 的
defaultControllerClass方法 - Unidraw 框架的
Creator::Create使用參數化 factory method 重建物件 - Orbix ORB 使用 Factory Method 產生適當類型的 proxy
相關模式(Related Patterns)#
- Abstract Factory 常以 Factory Method 實作
- Factory Method 通常在 Template Method 中被呼叫
- Prototype 不需要子類別化 Creator,但通常需要產品的 Initialize 操作;Factory Method 則不需要此操作