一、引言
在上一專題中介紹了工廠方法模式,工廠方法模式是為了克服簡單工廠模式的缺點而設計出來的,簡單工廠模式的工廠類隨着產品類的增加需要增加額外的代碼),而工廠方法模式每個具體工廠類只完成單個實例的創建,所以它具有很好的可擴展性。但是在現實生活中,一個工廠只創建單個產品這樣的例子很少,因為現在的工廠都多元化了,一個工廠創建一系列的產品,如果我們要設計這樣的系統時,工廠方法模式顯然在這里不適用,然后抽象工廠模式卻可以很好地解決一系列產品創建的問題,這是本專題所要介紹的內容。
二、抽象工廠詳細介紹
這里首先以一個生活中抽象工廠的例子來實現一個抽象工廠,然后再給出抽象工廠的定義和UML圖來幫助大家更好地掌握抽象工廠模式,同時大家在理解的時候,可以對照抽象工廠生活中例子的實現和它的定義來加深抽象工廠的UML圖理解。
2.1 抽象工廠的具體實現
下面就以生活中 “絕味” 連鎖店的例子來實現一個抽象工廠模式。例如,絕味鴨脖想在江西南昌和上海開分店,但是由於當地人的口味不一樣,在南昌的所有絕味的東西會做的辣一點,而上海不喜歡吃辣的,所以上海的所有絕味的東西都不會做的像南昌的那樣辣,然而這點不同導致南昌絕味工廠和上海的絕味工廠生成所有絕味的產品都不同,也就是某個具體工廠需要負責一系列產品(指的是絕味所有食物)的創建工作,下面就具體看看如何使用抽象工廠模式來實現這種情況。
1 /// <summary> 2 /// 下面以絕味鴨脖連鎖店為例子演示下抽象工廠模式 3 /// 因為每個地方的喜歡的口味不一樣,有些地方喜歡辣點的,有些地方喜歡吃不辣點 4 /// 客戶端調用 5 /// </summary> 6 class Client 7 { 8 static void Main(string[] args) 9 { 10 // 南昌工廠制作南昌的鴨脖和鴨架 11 AbstractFactory nanChangFactory = new NanChangFactory(); 12 YaBo nanChangYabo = nanChangFactory.CreateYaBo(); 13 nanChangYabo.Print(); 14 YaJia nanChangYajia= nanChangFactory.CreateYaJia(); 15 nanChangYajia.Print(); 16 17 // 上海工廠制作上海的鴨脖和鴨架 18 AbstractFactory shangHaiFactory = new ShangHaiFactory(); 19 shangHaiFactory.CreateYaBo().Print(); 20 shangHaiFactory.CreateYaJia().Print(); 21 22 Console.Read(); 23 } 24 } 25 26 /// <summary> 27 /// 抽象工廠類,提供創建兩個不同地方的鴨架和鴨脖的接口 28 /// </summary> 29 public abstract class AbstractFactory 30 { 31 // 抽象工廠提供創建一系列產品的接口,這里作為例子,只給出了絕味中鴨脖和鴨架的創建接口 32 public abstract YaBo CreateYaBo(); 33 public abstract YaJia CreateYaJia(); 34 } 35 36 /// <summary> 37 /// 南昌絕味工廠負責制作南昌的鴨脖和鴨架 38 /// </summary> 39 public class NanChangFactory : AbstractFactory 40 { 41 // 制作南昌鴨脖 42 public override YaBo CreateYaBo() 43 { 44 return new NanChangYaBo(); 45 } 46 // 制作南昌鴨架 47 public override YaJia CreateYaJia() 48 { 49 return new NanChangYaJia(); 50 } 51 } 52 53 /// <summary> 54 /// 上海絕味工廠負責制作上海的鴨脖和鴨架 55 /// </summary> 56 public class ShangHaiFactory : AbstractFactory 57 { 58 // 制作上海鴨脖 59 public override YaBo CreateYaBo() 60 { 61 return new ShangHaiYaBo(); 62 } 63 // 制作上海鴨架 64 public override YaJia CreateYaJia() 65 { 66 return new ShangHaiYaJia(); 67 } 68 } 69 70 /// <summary> 71 /// 鴨脖抽象類,供每個地方的鴨脖類繼承 72 /// </summary> 73 public abstract class YaBo 74 { 75 /// <summary> 76 /// 打印方法,用於輸出信息 77 /// </summary> 78 public abstract void Print(); 79 } 80 81 /// <summary> 82 /// 鴨架抽象類,供每個地方的鴨架類繼承 83 /// </summary> 84 public abstract class YaJia 85 { 86 /// <summary> 87 /// 打印方法,用於輸出信息 88 /// </summary> 89 public abstract void Print(); 90 } 91 92 /// <summary> 93 /// 南昌的鴨脖類,因為江西人喜歡吃辣的,所以南昌的鴨脖稍微會比上海做的辣 94 /// </summary> 95 public class NanChangYaBo : YaBo 96 { 97 public override void Print() 98 { 99 Console.WriteLine("南昌的鴨脖"); 100 } 101 } 102 103 /// <summary> 104 /// 上海的鴨脖沒有南昌的鴨脖做的辣 105 /// </summary> 106 public class ShangHaiYaBo : YaBo 107 { 108 public override void Print() 109 { 110 Console.WriteLine("上海的鴨脖"); 111 } 112 } 113 114 /// <summary> 115 /// 南昌的鴨架 116 /// </summary> 117 public class NanChangYaJia : YaJia 118 { 119 public override void Print() 120 { 121 Console.WriteLine("南昌的鴨架子"); 122 } 123 } 124 125 /// <summary> 126 /// 上海的鴨架 127 /// </summary> 128 public class ShangHaiYaJia : YaJia 129 { 130 public override void Print() 131 { 132 Console.WriteLine("上海的鴨架子"); 133 } 134 }
2.2 抽象工廠模式的定義和類圖
上面代碼中都有詳細的注釋,這里就不再解釋上面的代碼了,下面就具體看看抽象工廠模式的定義吧(理解定義可以參考上面的實現來加深理解):
抽象工廠模式:提供一個創建產品的接口來負責創建相關或依賴的對象,而不具體明確指定具體類
抽象工廠允許客戶使用抽象的接口來創建一組相關產品,而不需要知道或關心實際生產出的具體產品是什么。這樣客戶就可以從具體產品中被解耦。下面通過抽象工模式的類圖來了解各個類中之間的關系:
2.3 抽象工廠應對需求變更
看完上面抽象工廠的實現之后,如果 “絕味”公司又想在湖南開一家分店怎么辦呢? 因為湖南人喜歡吃麻辣的,下面就具體看看應用了抽象工廠模式的系統是如何應對這種需求的。
/// <summary> /// 如果絕味又想開一家湖南的分店時,因為湖南喜歡吃麻的 /// 所以這是有需要有一家湖南的工廠專門制作 /// </summary> public class HuNanFactory : AbstractFactory { // 制作湖南鴨脖 public override YaBo CreateYaBo() { return new HuNanYaBo(); } // 制作湖南鴨架 public override YaJia CreateYaJia() { return new HuNanYajia(); } } /// <summary> /// 湖南的鴨脖 /// </summary> public class HuNanYaBo : YaBo { public override void Print() { Console.WriteLine("湖南的鴨脖"); } } /// <summary> /// 湖南的鴨架 /// </summary> public class HuNanYajia : YaJia { public override void Print() { Console.WriteLine("湖南的鴨架子"); } }
此時,只需要添加三個類:一個是湖南具體工廠類,負責創建湖南口味的鴨脖和鴨架,另外兩個類是具有湖南口味的鴨脖類和鴨架類。從上面代碼看出,抽象工廠對於系列產品的變化支持 “開放——封閉”原則(指的是要求系統對擴展開放,對修改封閉),擴展起來非常簡便,但是,抽象工廠對於添加新產品這種情況就不支持”開放——封閉 “原則,這也是抽象工廠的缺點所在,這點會在第四部分詳細介紹。
三、抽象工廠的分析
抽象工廠模式將具體產品的創建延遲到具體工廠的子類中,這樣將對象的創建封裝起來,可以減少客戶端與具體產品類之間的依賴,從而使系統耦合度低,這樣更有利於后期的維護和擴展,這真是抽象工廠模式的優點所在,然后抽象模式同時也存在不足的地方。下面就具體看下抽象工廠的缺點(缺點其實在前面的介紹中以已經涉及了):
抽象工廠模式很難支持新種類產品的變化。這是因為抽象工廠接口中已經確定了可以被創建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣也就違背了“開發——封閉”原則。
知道了抽象工廠的優缺點之后,也就能很好地把握什么情況下考慮使用抽象工廠模式了,下面就具體看看使用抽象工廠模式的系統應該符合那幾個前提:
- 一個系統不要求依賴產品類實例如何被創建、組合和表達的表達,這點也是所有工廠模式應用的前提。
- 這個系統有多個系列產品,而系統中只消費其中某一系列產品
- 系統要求提供一個產品類的庫,所有產品以同樣的接口出現,客戶端不需要依賴具體實現。
四、.NET中抽象工廠模式實現
抽象工廠模式在實際中的應用也是相當頻繁的,然而在我們.NET類庫中也存在應用抽象工廠模式的類,這個類就是System.Data.Common.DbProviderFactory,這個類位於System.Data.dll程序集中,該類扮演抽象工廠模式中抽象工廠的角色,我們可以用reflector反編譯工具查看該類的實現:
/// 扮演抽象工廠的角色
/// 創建連接數據庫時所需要的對象集合,
/// 這個對象集合包括有 DbConnection對象(這個是抽象產品類,如絕味例子中的YaBo類)、DbCommand類、DbDataAdapter類,針對不同的具體工廠都需要實現該抽象類中方法,
public abstract class DbProviderFactory { // 提供了創建具體產品的接口方法 protected DbProviderFactory(); public virtual DbCommand CreateCommand(); public virtual DbCommandBuilder CreateCommandBuilder(); public virtual DbConnection CreateConnection(); public virtual DbConnectionStringBuilder CreateConnectionStringBuilder(); public virtual DbDataAdapter CreateDataAdapter(); public virtual DbDataSourceEnumerator CreateDataSourceEnumerator(); public virtual DbParameter CreateParameter(); public virtual CodeAccessPermission CreatePermission(PermissionState state); }
DbProviderFactory類是一個抽象工廠類,該類提供了創建數據庫連接時所需要的對象集合的接口,實際創建的工作在其子類工廠中進行,微軟使用的是SQL Server數據庫,因此提供了連接SQL Server數據的具體工廠實現,具體代碼可以用反編譯工具查看,具體代碼如下:
/// 扮演着具體工廠的角色,用來創建連接SQL Server數據所需要的對象 public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider { // Fields public static readonly SqlClientFactory Instance = new SqlClientFactory(); // 構造函數 private SqlClientFactory() { } // 重寫抽象工廠中的方法 public override DbCommand CreateCommand() { // 創建具體產品 return new SqlCommand(); } public override DbCommandBuilder CreateCommandBuilder() { return new SqlCommandBuilder(); } public override DbConnection CreateConnection() { return new SqlConnection(); } public override DbConnectionStringBuilder CreateConnectionStringBuilder() { return new SqlConnectionStringBuilder(); } public override DbDataAdapter CreateDataAdapter() { return new SqlDataAdapter(); } public override DbDataSourceEnumerator CreateDataSourceEnumerator() { return SqlDataSourceEnumerator.Instance; } public override DbParameter CreateParameter() { return new SqlParameter(); } public override CodeAccessPermission CreatePermission(PermissionState state) { return new SqlClientPermission(state); } }
因為微軟只給出了連接SQL Server的具體工廠的實現,我們也可以自定義連接Oracle、MySql的具體工廠的實現。
五、總結
到這里,抽象工廠模式的介紹就結束,在下一專題就將為大家介紹建造模式。
程序源碼:抽象工廠模式實現