意圖(Intent)#

定義一個建立物件的介面,但讓子類別決定要實體化哪一個類別。Factory Method 讓類別將實體化的動作延遲到子類別。

別名(Also Known As)#

Virtual Constructor

動機(Motivation)#

假設有一個支援多文件的應用程式框架,其中 Application 與 Document 都是抽象類別。要建立繪圖應用程式,需定義 DrawingApplication 與 DrawingDocument。Application 負責管理 Document,並在使用者選擇「開啟」或「新增」時建立它們。

問題在於:Application 只知道何時該建立 Document,卻不知道該建立哪一種 Document——因為具體的 Document 子類別是應用程式特有的。框架必須實體化類別,但它只認識抽象類別。

Factory Method 模式的解法是:讓 Application 子類別覆寫一個抽象的 CreateDocument 操作,回傳適當的 Document 子類別。我們稱 CreateDocumentfactory 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)#

參與者範例職責
ProductDocument定義 factory method 所建立物件的介面
ConcreteProductMyDocument實作 Product 介面
CreatorApplication宣告 factory method,回傳 Product 型別;可能提供預設的 factory method 實作;可能呼叫 factory method 來建立 Product
ConcreteCreatorMyApplication覆寫 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)#

  • MacAppET++ 中的 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 則不需要此操作