定義
抽象工廠模式的實質就是提供接口來創建一系列相關或獨立的對象而不指定這些對象的具體類。
理解
在軟件系統中,經常面臨著“一系列相互依賴的對象”的創建工作;同時由于需求的變化,往往存在著更多系列對象的創建工作。如何應對這種變化?如何繞過常規的對象的創建方法(熟悉的new操作符),提供一種“封裝機制”來避免客戶程序和這種“多系列具體對象創建工作”的緊耦合?這就是我們要說的抽象工廠模式。抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中,客戶端程序需要創建抽象工廠的具體實現,然后使用抽象工廠作為接口來創建這一主題的具體對象。客戶端程序不需要知道(或關心)它從這些內部的工廠方法中獲得對象的具體類型,因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。
結構

優點
缺點
適用情形
代碼示例說明
(這里借用TerryLee大神的例子和代碼)
中國企業需要一項簡單的財務計算:每月月底,財務人員要計算員工的工資。
員工的工資 = (基本工資 + 獎金 - 個人所得稅)。這是一個放之四海皆準的運算法則。
為了簡化系統,我們假設員工基本工資總是4000美金。
中國企業獎金和個人所得稅的計算規則是:
獎金 = 基本工資(4000) * 10%
個人所得稅 = (基本工資 + 獎金) * 40%
我們現在要為此構建一個軟件系統(代號叫Softo),滿足中國企業的需求。
此時采用最簡單的方式,類圖如下所示:

代碼如下所示:
using System;namespace ChineseSalary{ /// <summary> /// 計算中國個人所得稅 /// </summary> public class ChineseTax { public double Calculate() { return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4; } }}using System;namespace ChineseSalary{ /// <summary> /// 計算中國個人獎金 /// </summary> public class ChineseBonus { public double Calculate() { return Constant.BASE_SALARY * 0.1; } }}namespace ChineseSalary{ /// <summary> /// 公用的常量 /// </summary> public class Constant { public static double BASE_SALARY = 4000; }}using System;namespace ChineseSalary{ /// <summary> /// 客戶端程序調用 /// </summary> public class Calculator { public static void Main(string[] args) { ChineseBonus bonus = new ChineseBonus(); double bonusValue = bonus.Calculate(); ChineseTax tax = new ChineseTax(); double taxValue = tax.Calculate(); double salary = 4000 + bonusValue - taxValue; Console.WriteLine("Chinaese Salary is:" + salary); Console.ReadLine(); } }}美國企業工資的計算與中國大致相同,但是在獎金和個人所得稅計算規則不同于中國:
獎金 = 基本工資 * 15 %
個人所得稅 = (基本工資 * 5% + 獎金 * 25%)
我們僅僅將ChineseTax、ChineseBonus修改為AmericanTax、AmericanBonus。 只是修改了部分類的名稱和類方法的內容,結構沒有什么變化,修改后的類圖如下所示:

代碼如下所示:
using System;namespace AmericanSalary{ /// <summary> /// 計算美國個人獎金 /// </summary> public class AmericanBonus { public double Calculate() { return Constant.BASE_SALARY * 0.1; } }}using System;namespace AmericanSalary{ /// <summary> /// 計算美國個人所得稅 /// </summary> public class AmericanTax { public double Calculate() { return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4; } }}using System;namespace AmericanSalary{ /// <summary> /// 公用的常量 /// </summary> public class Constant { public static double BASE_SALARY = 4000; }}using System;namespace AmericanSalary{ /// <summary> /// 客戶端程序調用 /// </summary> public class Calculator { public static void Main(string[] args) { AmericanBonus bonus = new AmericanBonus(); double bonusValue = bonus.Calculate(); AmericanTax tax = new AmericanTax(); double taxValue = tax.Calculate(); double salary = 4000 + bonusValue - taxValue; Console.WriteLine("American Salary is:" + salary); Console.ReadLine(); } }}開始只考慮將Softo系統運行于中國企業,但隨著MaxDO公司業務向海外拓展, MaxDO需要將該系統移植給美國使用。需要將上面中國和美國兩種情況整合在一個系統。移植時,MaxDO不得不拋棄中國企業的業務規則類ChineseTax和ChineseBonus, 然后為美國企業新建兩個業務規則類: AmericanTax,AmericanBonus。最后修改了業務規則調用Calculator類。
結果我們發現:每當Softo系統移植的時候,就拋棄原來的類。現在,如果中國華為要購買該系統,我們不得不再次拋棄AmericanTax,AmericanBonus,修改回原來的業務規則。一個可以立即想到的做法就是在系統中保留所有業務規則模型,即保留中國和美國企業工資運算規則。
前面系統的整合問題在于:當系統在客戶在美國和中國企業間切換時仍然需要修改Caculator代碼。
一個維護性良好的系統應該遵循“開閉原則”。即:封閉對原來代碼的修改,開放對原來代碼的擴展(如類的繼承,接口的實現)
我們發現不論是中國企業還是美國企業,他們的業務運規則都采用同樣的計算接口。 于是很自然地想到建立兩個業務接口類Tax,Bonus,然后讓AmericanTax、AmericanBonus和ChineseTax、ChineseBonus分別實現這兩個接口,類圖如下所示:

具體代碼如下所示:
using System;namespace InterfaceSalary{ /// <summary> /// 獎金抽象類 /// </summary> public abstract class Bonus { public abstract double Calculate(); }}using System;namespace InterfaceSalary{ /// <summary> /// 計算中國個人獎金 /// </summary> public class ChineseBonus:Bonus { public override double Calculate() { return Constant.BASE_SALARY * 0.1; } }}using System;namespace InterfaceSalary{ /// <summary> /// 個人所得稅抽象類 /// </summary> public abstract class Tax { public abstract double Calculate(); }}using System;namespace InterfaceSalary{ /// <summary> /// 計算中國個人所得稅 /// </summary> public class ChineseTax:Tax { public override double Calculate() { return (Constant.BASE_SALARY + (Constant.BASE_SALARY * 0.1)) * 0.4; } }}using System;namespace InterfaceSalary{ /// <summary> /// 公用的常量 /// </summary> public class Constant { public static double BASE_SALARY = 4000; }}using System;namespace InterfaceSalary{ /// <summary> /// 客戶端程序調用 /// </summary> public class Calculator { public static void Main(string[] args) { Bonus bonus = new ChineseBonus(); double bonusValue = bonus.Calculate(); Tax tax = new ChineseTax(); double taxValue = tax.Calculate(); double salary = 4000 + bonusValue - taxValue; Console.WriteLine("Chinaese Salary is:" + salary); Console.ReadLine(); } }}然而,上面增加的接口幾乎沒有解決任何問題,因為當系統的客戶在美國和中國企業間切換時Caculator代碼仍然需要修改。只不過修改少了兩處,但是仍然需要修改ChineseBonus,ChineseTax部分。致命的問題是:我們需要將這個移植工作轉包給一個叫Hippo的軟件公司。 由于版權問題,我們并未提供Softo系統的源碼給Hippo公司,因此Hippo公司根本無法修改Calculator,導致實際上移植工作無法進行。
為此,我們考慮增加一個工具類(命名為Factory),代碼如下:
using System;namespace FactorySalary{ /// <summary> /// Factory類 /// </summary> public class Factory { public Tax CreateTax() { return new ChineseTax(); } public Bonus CreateBonus() { return new ChineseBonus(); } }}修改后的客戶端代碼:using System;namespace FactorySalary{ /// <summary> /// 客戶端程序調用 ///
新聞熱點
疑難解答