Factory Method Pattern(工廠方法模式)
是什麼?
Factory Method Pattern 定義一個建立物件的介面(或抽象方法),但把實際建立哪種物件的決定權交給子類別。呼叫端只透過介面操作產品,不需要知道具體的類別是什麼。
ℹ️GoF 分類
Factory Method 屬於建立型模式(Creational Pattern),重點在於將物件的建立邏輯延遲到子類別,達到解耦的效果。
什麼時候用?
用以下 if/then 條件判斷:
- 如果產品種類會持續增加,且你不想每次新增都修改既有的建立邏輯 → 用 Factory Method
- 如果框架需要讓使用者自訂要建立的元件類型(例如 ASP.NET 的 ControllerFactory)→ 用 Factory Method
- 如果建立邏輯散佈在程式碼多處,且每次新增產品都要改好幾個地方 → 用 Factory Method 集中管理
- 如果產品種類固定(1-2 種)且不會變 → 直接
new就好,不需要 Factory Method
什麼時候不該用?
⚠️過度設計警告
如果產品類型固定且不會擴展,直接 new 就好。Factory Method 會增加繼承層級,只有在「產品種類會增長」或「需要讓框架使用者自訂建立邏輯」時才值得用。
執行流程
定義產品介面
宣告所有產品共用的方法簽名
定義工廠介面
宣告 Factory Method(回傳產品介面型別)
實作具體產品
每種產品各自實作產品介面
實作具體工廠
每個工廠子類別覆寫 Factory Method,建立對應的產品
客戶端透過工廠建立
呼叫工廠方法取得產品,不需知道具體類別
流程解讀:Factory Method 的關鍵是「把 new 的決定權交給子類別」。抽象工廠定義了一個回傳產品介面的方法(Factory Method),但不決定具體建立什麼。每個具體工廠覆寫這個方法,各自決定要 new 哪種具體產品。這讓 Client 只依賴抽象,新增產品時只需新增工廠子類別和產品子類別,不改既有程式碼。
程式碼範例
C# 版本
// 1. 產品介面
public interface INotification
{
string Send(string message);
}
// 2. 具體產品
public class EmailNotification : INotification
{
public string Send(string message) => $"[Email] {message}";
}
public class SmsNotification : INotification
{
public string Send(string message) => $"[SMS] {message}";
}
public class PushNotification : INotification
{
public string Send(string message) => $"[Push] {message}";
}
// 3. 工廠介面
public abstract class NotificationFactory
{
public abstract INotification CreateNotification();
public string Notify(string message)
{
var notification = CreateNotification();
return notification.Send(message);
}
}
// 4. 具體工廠
public class EmailFactory : NotificationFactory
{
public override INotification CreateNotification() => new EmailNotification();
}
public class SmsFactory : NotificationFactory
{
public override INotification CreateNotification() => new SmsNotification();
}
// 5. 使用
NotificationFactory factory = new EmailFactory();
Console.WriteLine(factory.Notify("你的訂單已成立")); // [Email] 你的訂單已成立
factory = new SmsFactory();
Console.WriteLine(factory.Notify("驗證碼:1234")); // [SMS] 驗證碼:1234TypeScript 版本
// 1. 產品介面
interface Notification {
send(message: string): string;
}
// 2. 具體產品
class EmailNotification implements Notification {
send(message: string): string {
return `[Email] ${message}`;
}
}
class SmsNotification implements Notification {
send(message: string): string {
return `[SMS] ${message}`;
}
}
// 3. 工廠介面
abstract class NotificationFactory {
abstract createNotification(): Notification;
notify(message: string): string {
const notification = this.createNotification();
return notification.send(message);
}
}
// 4. 具體工廠
class EmailFactory extends NotificationFactory {
createNotification(): Notification {
return new EmailNotification();
}
}
class SmsFactory extends NotificationFactory {
createNotification(): Notification {
return new SmsNotification();
}
}
// 5. 使用
let factory: NotificationFactory = new EmailFactory();
console.log(factory.notify("你的訂單已成立")); // [Email] 你的訂單已成立
factory = new SmsFactory();
console.log(factory.notify("驗證碼:1234")); // [SMS] 驗證碼:1234Python 版本
from abc import ABC, abstractmethod
# 1. 產品介面
class Notification(ABC):
@abstractmethod
def send(self, message: str) -> str:
pass
# 2. 具體產品
class EmailNotification(Notification):
def send(self, message: str) -> str:
return f"[Email] {message}"
class SmsNotification(Notification):
def send(self, message: str) -> str:
return f"[SMS] {message}"
# 3. 工廠介面
class NotificationFactory(ABC):
@abstractmethod
def create_notification(self) -> Notification:
pass
def notify(self, message: str) -> str:
notification = self.create_notification()
return notification.send(message)
# 4. 具體工廠
class EmailFactory(NotificationFactory):
def create_notification(self) -> Notification:
return EmailNotification()
class SmsFactory(NotificationFactory):
def create_notification(self) -> Notification:
return SmsNotification()
# 5. 使用
factory: NotificationFactory = EmailFactory()
print(factory.notify("你的訂單已成立")) # [Email] 你的訂單已成立
factory = SmsFactory()
print(factory.notify("驗證碼:1234")) # [SMS] 驗證碼:1234Java 版本
// 1. 產品介面
public interface Notification {
String send(String message);
}
// 2. 具體產品
public class EmailNotification implements Notification {
public String send(String message) { return "[Email] " + message; }
}
public class SmsNotification implements Notification {
public String send(String message) { return "[SMS] " + message; }
}
// 3. 工廠介面
public abstract class NotificationFactory {
public abstract Notification createNotification();
public String notify(String message) {
Notification notification = createNotification();
return notification.send(message);
}
}
// 4. 具體工廠
public class EmailFactory extends NotificationFactory {
public Notification createNotification() { return new EmailNotification(); }
}
public class SmsFactory extends NotificationFactory {
public Notification createNotification() { return new SmsNotification(); }
}
// 5. 使用
NotificationFactory factory = new EmailFactory();
System.out.println(factory.notify("你的訂單已成立")); // [Email] 你的訂單已成立
factory = new SmsFactory();
System.out.println(factory.notify("驗證碼:1234")); // [SMS] 驗證碼:1234結構圖
結構解讀:Client 只認識 NotificationFactory(抽象)和 Notification(介面),不知道具體實作是什麼。EmailFactory 覆寫 Factory Method 來建立 EmailNotification,SmsFactory 建立 SmsNotification。新增 PushNotification 時只需新增 PushFactory + PushNotification,Client 和既有工廠完全不用改。
實戰補充
💡資深開發者經驗
框架中的 Factory Method:ASP.NET Core 的 IControllerFactory、Java 的 BeanFactory、Angular 的 ComponentFactoryResolver 都是 Factory Method 的應用。框架定義介面,使用者透過實作來自訂行為。
搭配 DI:在 .NET 中,你可以註冊一個 Func<INotification> 或 INotificationFactory,讓 DI 容器負責管理工廠的生命週期,避免手動 new Factory。
Simple Factory vs Factory Method:Simple Factory 用一個 switch/if 決定建什麼(不是 GoF 模式),Factory Method 用繼承讓子類別決定。當產品種類穩定用 Simple Factory 即可,會擴增就用 Factory Method。
理解測驗
🤔 Factory Method 和直接在程式碼中 new 物件相比,最大的好處是什麼?
🤔 在 Factory Method Pattern 中,誰決定要建立哪種具體產品?
🤔 以下哪個場景最適合用 Factory Method?
面試常見問題
Q: Simple Factory、Factory Method、Abstract Factory 三者怎麼區分?
A: Simple Factory 用一個 switch/if 決定建什麼(不是 GoF 模式,適合產品固定的場景)。Factory Method 用繼承讓子類別各自決定建一種產品(適合產品會增加)。Abstract Factory 用介面讓具體工廠建一整套相關產品(適合產品有家族概念)。選擇路徑:產品只有一種且固定 → Simple Factory;產品會增加但各自獨立 → Factory Method;產品有家族且必須搭配 → Abstract Factory。
Q: Factory Method 和 DI 容器有什麼關係?
A: DI 容器本質上就是一個超級工廠,根據介面型別回傳對應的具體實作。在 .NET 中,services.AddScoped<INotification, EmailNotification>() 就是在「註冊」工廠方法。差異在於 DI 容器是通用的,Factory Method 是針對特定產品家族設計的。
相關模式
| 模式 | 關係 | |------|------| | Abstract Factory | Abstract Factory 內部通常用多個 Factory Method 來建立每種產品 | | Template Method | Factory Method 常出現在 Template Method 內部 — 模板定義流程,其中一步是 Factory Method 建立需要的物件 | | Prototype | 都是建立型模式,但 Prototype 透過複製建立,Factory Method 透過子類別覆寫建立 | | Strategy | Factory 可以用 Strategy 來決定建立邏輯,而非用繼承 |
重點整理
💡一句話記住
Factory Method = 把 new 的決定權交給子類別。 口訣:「父類別定框架,子類別填產品」
| 概念 | 說明 | |------|------| | Product(產品介面) | 定義產品的共用方法 | | ConcreteProduct(具體產品) | 各自實作產品介面 | | Creator(工廠抽象類別) | 宣告 Factory Method,可包含業務邏輯 | | ConcreteCreator(具體工廠) | 覆寫 Factory Method,建立特定產品 | | 核心好處 | 開放封閉原則 — 新增產品不用改既有程式碼 | | 代價 | 每新增一種產品就要多一個工廠子類別 |