抽象工廠模式提供「建立一系列相關物件的介面,無需指定具體類別」。本文以水果工廠為例,展示四種實作演進:硬編碼 Switch、參數化工廠、反射動態建立、現代 DI 容器整合,並比較優缺點。
public interface IFruit { string Name { get; } }
public class Apple : IFruit { public string Name => "蘋果"; }
public class Orange : IFruit { public string Name => "橘子"; }
public class Pear : IFruit { public string Name => "梨子"; }
public class FruitFactoryV1
{
public IFruit CreateFruit(string name) => name switch
{
"Apple" => new Apple(),
"Orange" => new Orange(),
_ => throw new ArgumentException($"Unknown fruit: {name}")
};
}
缺點:新增水果需修改工廠類別,違反開放封閉原則。
public class FruitFactoryV2
{
public IFruit CreateFruit(string name) => name switch
{
"Apple" => new Apple(),
"Orange" => new Orange(),
"Pear" => new Pear(), // 仍需修改此處
_ => null
};
}
客戶端程式碼簡化,但工廠仍需修改。
public class FruitFactoryV3
{
private readonly Dictionary _fruitTypes = new();
public FruitFactoryV3()
{
// 掃描組件中實作 IFruit 的非抽象類別
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
if (typeof(IFruit).IsAssignableFrom(type) && !type.IsAbstract && !type.IsInterface)
{
_fruitTypes[type.Name] = type; // 命名慣例:類別名 = 水果名
}
}
}
public IFruit CreateFruit(string name)
{
if (_fruitTypes.TryGetValue(name, out var type))
{
return (IFruit)Activator.CreateInstance(type);
}
throw new ArgumentException($"Unknown fruit: {name}");
}
}
優點:新增水果只需新增類別,無需修改工廠。缺點:依賴命名慣例、效能稍低、無法處理建構函式參數。
public class FruitFactoryV4
{
private readonly Dictionary> _creators = new();
public void Register(string name) where T : IFruit, new()
{
_creators[name] = () => new T();
}
public void Register(string name, Func factory)
{
_creators[name] = factory;
}
public IFruit CreateFruit(string name)
{
if (_creators.TryGetValue(name, out var creator))
return creator();
throw new ArgumentException($"Unknown fruit: {name}");
}
}
// 使用範例
var factory = new FruitFactoryV4();
factory.Register("Apple");
factory.Register("Orange");
// 甚至支援複雜建構邏輯
factory.Register("SpecialPear", () => new Pear { /* 初始化 */ });
using Microsoft.Extensions.DependencyInjection;
public interface IFruitFactory
{
IFruit Create(string name);
}
public class DiFruitFactory : IFruitFactory
{
private readonly IServiceProvider _sp;
private readonly Dictionary _map = new();
public DiFruitFactory(IServiceProvider sp)
{
_sp = sp;
// 掃描已註冊的服務
foreach (var svc in sp.GetServices())
{
_map[svc.GetType().Name] = svc.GetType();
}
}
public IFruit Create(string name)
{
if (_map.TryGetValue(name, out var type))
return (IFruit)_sp.GetRequiredService(type);
throw new ArgumentException($"Unknown fruit: {name}");
}
}
// 註冊
var services = new ServiceCollection();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService();
var apple = factory.Create("Apple");
| 版本 | 符合 OCP | 型別安全 | 支援建構參數 | 效能 | 適用場景 |
|---|---|---|---|---|---|
| V1 Switch | ❌ | ✅ | ✅ | 最高 | 極簡單、固定類別 |
| V2 參數化 | ❌ | ✅ | ✅ | 高 | 客戶端簡化,但仍需修改工廠 |
| V3 反射 | ✅ | ❌ (執行時) | ❌ (無參建構) | 中 (首次較慢) | 插件系統、動態擴充 |
| V4 委派註冊 | ✅ | ✅ | ✅ | 高 | 框架開發、需靈活建構邏輯 |
| V5 DI 容器 | ✅ | ✅ | ✅ (完整 DI) | 高 (容器優化) | 企業級應用、微服務、現代 .NET |
IFruitFactory 介面,讓使用者自行註冊IServiceProvider 或 IServiceScopeFactorynew 具體類別,統一透過工廠抽象抽象工廠模式核心在於「將物件建立邏輯封裝並抽象化」。從硬編碼演進到反射、委派註冊、DI 容器,本質都是延遲綁定具體類別。現代 .NET 開發首選 DI 容器整合,既符合 SOLID 原則,又獲得生命週期管理、攔截、AOP 等額外紅利。