意圖(Intent)#
提供一個介面,用於建立一系列相關或相互依賴的物件,而不需要指定它們的具體類別。
別名(Also Known As)#
Kit
動機(Motivation)#
假設要開發一套支援多種外觀風格(look-and-feel)的 UI 工具組,例如 Motif 與 Presentation Manager。不同風格對捲軸、視窗、按鈕等 widget 有不同的外觀與行為。如果在應用程式中到處直接實體化特定風格的 widget 類別,日後要切換風格就會非常困難。
解決方法是定義一個抽象的 WidgetFactory 類別,宣告建立各種 widget 的介面。每種外觀風格各有一個對應的 ConcreteFactory(如 MotifWidgetFactory、PMWidgetFactory),負責實體化該風格的 widget。Client 只透過 WidgetFactory 介面取得 widget,完全不知道底層的具體類別。
Abstract Factory 的核心價值在於:讓 client 與具體產品類別完全解耦,同時確保同一家族的產品能一致地搭配使用。
適用場景(Applicability)#
- 系統需要與產品的建立、組合、表示方式無關
- 系統需在多個產品家族之間切換配置
- 一組相關產品物件被設計為一起使用,且需要強制這個約束
- 想提供產品的類別庫,但只暴露介面,不暴露實作
結構(Structure)#
AbstractFactory 宣告建立各種 AbstractProduct 的介面。每個 ConcreteFactory 實作這些操作,產出對應的 ConcreteProduct。Client 僅透過抽象介面與工廠和產品互動,不依賴任何具體類別。
classDiagram
class AbstractFactory {
<<interface>>
+CreateProductA()
+CreateProductB()
}
class ConcreteFactory1 {
+CreateProductA()
+CreateProductB()
}
class ConcreteFactory2 {
+CreateProductA()
+CreateProductB()
}
class AbstractProductA {
<<interface>>
}
class AbstractProductB {
<<interface>>
}
class ProductA1
class ProductA2
class ProductB1
class ProductB2
AbstractFactory <|.. ConcreteFactory1
AbstractFactory <|.. ConcreteFactory2
AbstractProductA <|.. ProductA1
AbstractProductA <|.. ProductA2
AbstractProductB <|.. ProductB1
AbstractProductB <|.. ProductB2
ConcreteFactory1 ..> ProductA1 : creates
ConcreteFactory1 ..> ProductB1 : creates
ConcreteFactory2 ..> ProductA2 : creates
ConcreteFactory2 ..> ProductB2 : creates
Client --> AbstractFactory
Client --> AbstractProductA
Client --> AbstractProductB參與者(Participants)#
| 參與者 | 範例 | 職責 |
|---|---|---|
| AbstractFactory | WidgetFactory | 宣告建立各種抽象產品物件的介面 |
| ConcreteFactory | MotifWidgetFactory、PMWidgetFactory | 實作建立具體產品物件的操作 |
| AbstractProduct | Window、ScrollBar | 宣告某一類產品的介面 |
| ConcreteProduct | MotifWindow、MotifScrollBar | 定義由對應 ConcreteFactory 建立的產品物件;實作 AbstractProduct 介面 |
| Client | - | 僅使用 AbstractFactory 與 AbstractProduct 所宣告的介面 |
協作方式(Collaborations)#
- 執行時通常只建立一個 ConcreteFactory 實例。要建立不同的產品物件,client 應使用不同的 ConcreteFactory
- AbstractFactory 將產品物件的建立延遲到 ConcreteFactory 子類別
優缺點(Consequences)#
優點:
- 隔離具體類別 —— 工廠封裝了建立產品的職責,client 程式碼中不會出現具體產品的類別名稱
- 方便切換產品家族 —— ConcreteFactory 在應用程式中只出現一次(實體化的地方),更換工廠就能切換整個產品家族
- 確保產品一致性 —— 同一工廠產出的產品天生就能搭配使用,不會出現混搭不同家族的問題
缺點:
- 難以支援新種類的產品 —— AbstractFactory 介面固定了可建立的產品集合。新增產品種類需要修改 AbstractFactory 及其所有子類別
每當需要新增一種產品類型時,必須修改 AbstractFactory 介面及所有 ConcreteFactory,這是此模式最大的擴展性限制。可考慮使用參數化工廠方法來緩解。
實作要點(Implementation)#
- 工廠通常是 Singleton —— 每個產品家族只需一個 ConcreteFactory 實例,因此常以 Singleton 模式實作
- 使用 Factory Method 建立產品 —— 最常見的做法是在 AbstractFactory 中為每種產品定義一個 Factory Method,由 ConcreteFactory 覆寫。簡單但每個產品家族都需要一個子類別
- 使用 Prototype 建立產品 —— 當產品家族很多時,可用原型實例初始化工廠,透過 clone 產生新物件,避免為每個家族建立新的工廠子類別
- 參數化工廠 —— 用單一
Make操作搭配參數來指定產品種類,提高彈性但犧牲型別安全性。所有產品以相同的抽象介面回傳,client 無法區分具體型別
如果產品家族數量龐大且變動頻繁,考慮使用 Prototype 方式實作工廠——以原型字典取代大量子類別,新增家族只需註冊新的原型組合。
已知應用(Known Uses)#
- InterViews 框架使用 “Kit” 後綴命名 AbstractFactory 類別(如 WidgetKit、DialogKit),產生不同風格的 UI 物件
- ET++ 透過 Abstract Factory 達成跨視窗系統(X Windows、SunView)的可移植性,WindowSystem 抽象基底類別定義建立視窗系統資源的介面
相關模式(Related Patterns)#
- AbstractFactory 常以 Factory Method 實作,也可以用 Prototype 實作
- ConcreteFactory 通常是 Singleton