工廠方法解決單一物件創建變化,但現實系統常面臨「一組相互依賴、必須協同工作的物件」創建需求:UI 庫的按鈕/文字框/視窗必須同風格;資料存取的連線/命令/參數/適配器必須同 Provider;遊戲場景的道路/房屋/管道必須同風格。抽象工廠模式(Abstract Factory Pattern)應運而生:提供一個創建一系列相關或相互依賴物件的介面,而無需指定它們具體的類別(GoF)。


同一套沖壓設備(AbstractFactory),換模具(ConcreteFactory)即可生產不同車型的右車門、左車門、擋泥板、引擎蓋(Product Family)。模具確保同車型零件規格一致、裝配匹配。

public class ChineseBonus { public double Calculate() => 4000 * 0.1; }
public class ChineseTax { public double Calculate() => (4000 + 400) * 0.4; }
public class Calculator {
public void Compute() {
var bonus = new ChineseBonus().Calculate();
var tax = new ChineseTax().Calculate();
Console.WriteLine($"Salary: {4000 + bonus - tax}"); // 2640
}
}

複製整套類別改名 AmericanBonus、AmericanTax,修改 Calculator 引用。缺點:原始碼重複、切換需改代碼、無法動態擴展新國家。

public interface IBonus { double Calculate(); }
public interface ITax { double Calculate(); }
public class ChineseBonus : IBonus { /* ... */ }
public class AmericanBonus : IBonus { /* ... */ }
// Calculator 仍需 new ChineseBonus() 或 new AmericanBonus(),仍有硬編碼

public class Factory {
public ITax CreateTax() => new ChineseTax(); // 切換國家仍需修改此類
public IBonus CreateBonus() => new ChineseBonus();
}
public abstract class AbstractFactory {
public static AbstractFactory GetInstance() {
string factoryName = ConfigurationManager.AppSettings["factoryName"];
return string.IsNullOrEmpty(factoryName) ? null
: (AbstractFactory)Assembly.Load(factoryName).CreateInstance(factoryName);
}
public abstract ITax CreateTax();
public abstract IBonus CreateBonus();
}
public class ChineseFactory : AbstractFactory {
public override ITax CreateTax() => new ChineseTax();
public override IBonus CreateBonus() => new ChineseBonus();
}
public class AmericanFactory : AbstractFactory { /* 美國實作 */ }
// 新增 JapaneseFactory 只需新增類別,零修改既有代碼!

// 客戶端零硬編碼
var factory = AbstractFactory.GetInstance();
var bonus = factory.CreateBonus().Calculate();
var tax = factory.CreateTax().Calculate();
Console.WriteLine($"Salary: {4000 + bonus - tax}");
新增日本支援:只需加 JapaneseFactory 類別 + 修改配置檔,完全符合開放封閉原則。
DbProviderFactorySqlClientFactory、OracleClientFactory、OleDbFactory、OdbcFactory
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
using (var conn = factory.CreateConnection()) { conn.ConnectionString = "..."; conn.Open(); }
using (var cmd = factory.CreateCommand()) { cmd.Connection = conn; cmd.CommandText = "SELECT * FROM Users"; }
using (var adapter = factory.CreateDataAdapter()) { adapter.SelectCommand = cmd; adapter.Fill(dataSet); }
應用程式完全脫離具體資料庫類型,僅依賴 DbProviderFactory 抽象介面。
asp:、custom:)選擇對應的 ControlBuilder 工廠Clone()(適合產品實例化昂貴、狀態複雜)抽象工廠最大弱點:難以支援「新產品類型」。若需新增 IReport 產品,必須:
AbstractFactory 加入 CreateReport()ConcreteFactory 實作新方法李建忠老師指出:「對於新系列加新對象,目前還沒有完美的做法,這種變化實在太劇烈了」。緩解策略:參數化工廠方法 CreateProduct(ProductType type),但犧牲型別安全。
以下情況適用抽象工廠:
| 優點 | 缺點 |
|---|---|
| 分離具體類別,客戶端零硬編碼 | 難以擴展新產品類型(需改介面與所有工廠) |
| 易於切換產品族(單點修改/配置) | 介面膨脹:產品類型多時 AbstractFactory 方法過多 |
| 強制產品族一致性(避免混搭) | 複雜度較高:需維護雙重平行階層 |
| 利於團隊分工(介面契約為界) | 過度設計風險:單一產品族無需此模式 |
抽象工廠模式是「面向介面編程」在物件創建層面的極致體現。它不創建單一物件,而是創建「經過驗證、相互匹配的物件家族」。從 ADO.NET Provider 到跨平台 UI 庫、從雲端資源佈建到遊戲場景風格系統,抽象工廠無處不在。掌握它,意味著你理解了如何用「配置驅動實作選擇」取代「硬編碼綁定」,這是構建可擴展、可維護、可部署企業級系統的核心能力。