Abstract Factory Pattern(抽象工廠模式)

是什麼?

Abstract Factory Pattern 提供一個介面來建立一系列相關或相依的物件,而不需要指定它們的具體類別。每個具體工廠負責產出一整套風格一致的產品。

ℹ️GoF 分類

Abstract Factory 屬於建立型模式(Creational Pattern),重點在於建立一系列相關物件,確保產品之間的一致性。

什麼時候用?

用以下 if/then 條件判斷:

什麼時候不該用?

⚠️過度設計警告

如果你的產品不存在「家族」概念(產品之間沒有相依性),用 Abstract Factory 只會增加不必要的介面和類別。單一產品的建立用 Factory Method 就夠了,不需要升級到 Abstract Factory。

執行流程

1

定義產品介面

為每種產品定義各自的介面(如 IButton、ITextBox)

2

定義抽象工廠

宣告建立每種產品的方法(CreateButton、CreateTextBox)

3

實作具體產品

每個家族各自實作所有產品介面

4

實作具體工廠

每個具體工廠產出同一家族的所有產品

5

客戶端透過抽象工廠操作

切換工廠就切換整套產品,客戶端程式碼不用改

流程解讀:Abstract Factory 的核心是「產品家族的一致性保證」。抽象工廠定義了建立每種產品的方法,每個具體工廠負責產出同一家族的全套產品。Client 只透過抽象工廠和產品介面操作,切換家族只需換具體工廠。結構上的限制確保你不可能拿到 WindowsButton + MacTextBox 的混搭組合。

程式碼範例

C# 版本

csharp
// 1. 產品介面
public interface IButton
{
    string Render();
}
 
public interface ITextBox
{
    string Render();
}
 
// 2. Windows 家族
public class WindowsButton : IButton
{
    public string Render() => "[Windows Button] 扁平化風格按鈕";
}
 
public class WindowsTextBox : ITextBox
{
    public string Render() => "[Windows TextBox] 方角輸入框";
}
 
// 3. macOS 家族
public class MacButton : IButton
{
    public string Render() => "[Mac Button] 圓角漸層按鈕";
}
 
public class MacTextBox : ITextBox
{
    public string Render() => "[Mac TextBox] 圓角輸入框";
}
 
// 4. 抽象工廠
public interface IUIFactory
{
    IButton CreateButton();
    ITextBox CreateTextBox();
}
 
// 5. 具體工廠
public class WindowsUIFactory : IUIFactory
{
    public IButton CreateButton() => new WindowsButton();
    public ITextBox CreateTextBox() => new WindowsTextBox();
}
 
public class MacUIFactory : IUIFactory
{
    public IButton CreateButton() => new MacButton();
    public ITextBox CreateTextBox() => new MacTextBox();
}
 
// 6. 使用
IUIFactory factory = new MacUIFactory();
var button = factory.CreateButton();
var textBox = factory.CreateTextBox();
Console.WriteLine(button.Render());  // [Mac Button] 圓角漸層按鈕
Console.WriteLine(textBox.Render()); // [Mac TextBox] 圓角輸入框

TypeScript 版本

typescript
// 1. 產品介面
interface Button {
  render(): string;
}
 
interface TextBox {
  render(): string;
}
 
// 2. Windows 家族
class WindowsButton implements Button {
  render(): string { return "[Windows Button] 扁平化風格按鈕"; }
}
 
class WindowsTextBox implements TextBox {
  render(): string { return "[Windows TextBox] 方角輸入框"; }
}
 
// 3. macOS 家族
class MacButton implements Button {
  render(): string { return "[Mac Button] 圓角漸層按鈕"; }
}
 
class MacTextBox implements TextBox {
  render(): string { return "[Mac TextBox] 圓角輸入框"; }
}
 
// 4. 抽象工廠
interface UIFactory {
  createButton(): Button;
  createTextBox(): TextBox;
}
 
// 5. 具體工廠
class WindowsUIFactory implements UIFactory {
  createButton(): Button { return new WindowsButton(); }
  createTextBox(): TextBox { return new WindowsTextBox(); }
}
 
class MacUIFactory implements UIFactory {
  createButton(): Button { return new MacButton(); }
  createTextBox(): TextBox { return new MacTextBox(); }
}
 
// 6. 使用
const factory: UIFactory = new MacUIFactory();
console.log(factory.createButton().render());  // [Mac Button] 圓角漸層按鈕
console.log(factory.createTextBox().render()); // [Mac TextBox] 圓角輸入框

Python 版本

python
from abc import ABC, abstractmethod
 
# 1. 產品介面
class Button(ABC):
    @abstractmethod
    def render(self) -> str:
        pass
 
class TextBox(ABC):
    @abstractmethod
    def render(self) -> str:
        pass
 
# 2. Windows 家族
class WindowsButton(Button):
    def render(self) -> str:
        return "[Windows Button] 扁平化風格按鈕"
 
class WindowsTextBox(TextBox):
    def render(self) -> str:
        return "[Windows TextBox] 方角輸入框"
 
# 3. macOS 家族
class MacButton(Button):
    def render(self) -> str:
        return "[Mac Button] 圓角漸層按鈕"
 
class MacTextBox(TextBox):
    def render(self) -> str:
        return "[Mac TextBox] 圓角輸入框"
 
# 4. 抽象工廠
class UIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button:
        pass
 
    @abstractmethod
    def create_text_box(self) -> TextBox:
        pass
 
# 5. 具體工廠
class WindowsUIFactory(UIFactory):
    def create_button(self) -> Button:
        return WindowsButton()
 
    def create_text_box(self) -> TextBox:
        return WindowsTextBox()
 
class MacUIFactory(UIFactory):
    def create_button(self) -> Button:
        return MacButton()
 
    def create_text_box(self) -> TextBox:
        return MacTextBox()
 
# 6. 使用
factory: UIFactory = MacUIFactory()
print(factory.create_button().render())   # [Mac Button] 圓角漸層按鈕
print(factory.create_text_box().render()) # [Mac TextBox] 圓角輸入框

Java 版本

java
// 1. 產品介面
public interface Button { String render(); }
public interface TextBox { String render(); }
 
// 2. Windows 家族
public class WindowsButton implements Button {
    public String render() { return "[Windows Button] 扁平化風格按鈕"; }
}
public class WindowsTextBox implements TextBox {
    public String render() { return "[Windows TextBox] 方角輸入框"; }
}
 
// 3. macOS 家族
public class MacButton implements Button {
    public String render() { return "[Mac Button] 圓角漸層按鈕"; }
}
public class MacTextBox implements TextBox {
    public String render() { return "[Mac TextBox] 圓角輸入框"; }
}
 
// 4. 抽象工廠
public interface UIFactory {
    Button createButton();
    TextBox createTextBox();
}
 
// 5. 具體工廠
public class WindowsUIFactory implements UIFactory {
    public Button createButton() { return new WindowsButton(); }
    public TextBox createTextBox() { return new WindowsTextBox(); }
}
 
public class MacUIFactory implements UIFactory {
    public Button createButton() { return new MacButton(); }
    public TextBox createTextBox() { return new MacTextBox(); }
}
 
// 6. 使用
UIFactory factory = new MacUIFactory();
System.out.println(factory.createButton().render());
System.out.println(factory.createTextBox().render());

結構圖

Client
uses
UIFactory (interface)
creates
WindowsUIFactory / MacUIFactory
implements
Button (interface)
TextBox (interface)
WindowsButton, MacButton, ...

結構解讀:Client 只依賴 UIFactory 介面和產品介面(Button、TextBox),不知道具體實作。每個 ConcreteFactory(WindowsUIFactory、MacUIFactory)負責產出同一家族的所有產品。切換平台只需換工廠實例,Client 程式碼完全不用改。新增家族(如 LinuxUIFactory)只需新增工廠和對應產品,但新增產品種類(如 Checkbox)需要修改所有工廠介面。

實戰補充

💡資深開發者經驗

跨平台 UI:React Native、Flutter 底層都運用了類似 Abstract Factory 的概念 — 同一套 API,在不同平台渲染不同的原生元件。

資料庫 Provider 切換:ADO.NET 的 DbProviderFactory 就是 Abstract Factory。切換 SqlClientFactoryNpgsqlFactory,就能在 SQL Server 和 PostgreSQL 之間切換,業務程式碼完全不用改。

與 Factory Method 的差別:Factory Method 建立「一種」產品,Abstract Factory 建立「一整套」產品。如果你只需要建立單一物件,Factory Method 就夠了。

理解測驗

🤔 Abstract Factory 和 Factory Method 最大的差異是什麼?

🤔 為什麼 Abstract Factory 能確保產品之間的一致性?

🤔 以下哪個場景最適合用 Abstract Factory?

面試常見問題

Q: Abstract Factory 最大的缺點是什麼?如何緩解?

A: 新增產品種類時,所有工廠介面和具體工廠都要修改。例如原本只有 Button 和 TextBox,新增 Checkbox 時,IUIFactory 要加方法、WindowsUIFactory 和 MacUIFactory 都要實作。緩解方式:(1) 用泛型 Factory Method 如 Create<T>();(2) 改用 Prototype Registry,每個家族用一組原型物件。

Q: 實務中 Abstract Factory 最常見的應用場景是什麼?

A: 跨平台 UI 框架和資料庫 Provider 切換。ADO.NET 的 DbProviderFactory 就是 Abstract Factory — SqlClientFactory 產出 SqlConnection + SqlCommand,NpgsqlFactory 產出 NpgsqlConnection + NpgsqlCommand,業務程式碼透過抽象介面操作,切換資料庫只需換 Factory。

相關模式

| 模式 | 關係 | |------|------| | Factory Method | Abstract Factory 內部通常包含多個 Factory Method,各負責建立一種產品 | | Singleton | Abstract Factory 本身常實作為 Singleton,確保全域只有一個工廠入口 | | Prototype | 可以替代 Abstract Factory — 用原型物件的 Clone 來建立產品,不需要每個家族都寫一個工廠類別 | | Builder | 都是建立型模式,但 Builder 關注「建構步驟」,Abstract Factory 關注「產品家族一致性」 |

重點整理

💡一句話記住

Abstract Factory = IKEA 風格套組:選一個風格,整套家具自動搭配。 口訣:「換工廠換全套,混搭免擔心」

| 概念 | 說明 | |------|------| | AbstractFactory(抽象工廠) | 定義建立每種產品的方法介面 | | ConcreteFactory(具體工廠) | 產出同一家族的所有產品 | | AbstractProduct(抽象產品) | 每種產品的介面 | | ConcreteProduct(具體產品) | 特定家族的產品實作 | | 核心好處 | 保證產品家族一致性 + 切換整套產品只需換工廠 | | 代價 | 新增產品種類時,所有工廠都要改(介面擴增) |

你可能也想看

Factory Method PatternBuilder Pattern

按 ← → 鍵切換課程