一、引言
在上一專題中介紹了工廠方法模式,工廠方法模式是為了克服簡單工廠模式的缺點而設計出來的,簡單工廠模式的工廠類隨著產品類的增加需要增加額外的程式碼),而工廠方法模式每個具體工廠類只完成單個例項的建立,所以它具有很好的可擴充套件性。但是在現實生活中,一個工廠只建立單個產品這樣的例子很少,因為現在的工廠都多元化了,一個工廠建立一系列的產品,如果我們要設計這樣的系統時,工廠方法模式顯然在這裡不適用,然後抽象工廠模式卻可以很好地解決一系列產品建立的問題,這是本專題所要介紹的內容。
二、抽象工廠詳細介紹
這裡首先以一個生活中抽象工廠的例子來實現一個抽象工廠,然後再給出抽象工廠的定義和UML圖來幫助大家更好地掌握抽象工廠模式,同時大家在理解的時候,可以對照抽象工廠生活中例子的實現和它的定義來加深抽象工廠的UML圖理解。
2.1 抽象工廠的具體實現
下面就以生活中 “絕味” 連鎖店的例子來實現一個抽象工廠模式。例如,絕味鴨脖想在江西南昌和上海開分店,但是由於當地人的口味不一樣,在南昌的所有絕味的東西會做的辣一點,而上海不喜歡吃辣的,所以上海的所有絕味的東西都不會做的像南昌的那樣辣,然而這點不同導致南昌絕味工廠和上海的絕味工廠生成所有絕味的產品都不同,也就是某個具體工廠需要負責一系列產品(指的是絕味所有食物)的建立工作,下面就具體看看如何使用抽象工廠模式來實現這種情況。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
/// <summary> /// 下面以絕味鴨脖連鎖店為例子演示下抽象工廠模式 /// 因為每個地方的喜歡的口味不一樣,有些地方喜歡辣點的,有些地方喜歡吃不辣點 /// 客戶端呼叫 /// </summary> class Client { static void Main(string[] args) { // 南昌工廠製作南昌的鴨脖和鴨架 AbstractFactory nanChangFactory = new NanChangFactory(); YaBo nanChangYabo = nanChangFactory.CreateYaBo(); nanChangYabo.Print(); YaJia nanChangYajia= nanChangFactory.CreateYaJia(); nanChangYajia.Print(); // 上海工廠製作上海的鴨脖和鴨架 AbstractFactory shangHaiFactory = new ShangHaiFactory(); shangHaiFactory.CreateYaBo().Print(); shangHaiFactory.CreateYaJia().Print(); Console.Read(); } } /// <summary> /// 抽象工廠類,提供建立兩個不同地方的鴨架和鴨脖的介面 /// </summary> public abstract class AbstractFactory { // 抽象工廠提供建立一系列產品的介面,這裡作為例子,只給出了絕味中鴨脖和鴨架的建立介面 public abstract YaBo CreateYaBo(); public abstract YaJia CreateYaJia(); } /// <summary> /// 南昌絕味工廠負責製作南昌的鴨脖和鴨架 /// </summary> public class NanChangFactory : AbstractFactory { // 製作南昌鴨脖 public override YaBo CreateYaBo() { return new NanChangYaBo(); } // 製作南昌鴨架 public override YaJia CreateYaJia() { return new NanChangYaJia(); } } /// <summary> /// 上海絕味工廠負責製作上海的鴨脖和鴨架 /// </summary> public class ShangHaiFactory : AbstractFactory { // 製作上海鴨脖 public override YaBo CreateYaBo() { return new ShangHaiYaBo(); } // 製作上海鴨架 public override YaJia CreateYaJia() { return new ShangHaiYaJia(); } } /// <summary> /// 鴨脖抽象類,供每個地方的鴨脖類繼承 /// </summary> public abstract class YaBo { /// <summary> /// 列印方法,用於輸出資訊 /// </summary> public abstract void Print(); } /// <summary> /// 鴨架抽象類,供每個地方的鴨架類繼承 /// </summary> public abstract class YaJia { /// <summary> /// 列印方法,用於輸出資訊 /// </summary> public abstract void Print(); } /// <summary> /// 南昌的鴨脖類,因為江西人喜歡吃辣的,所以南昌的鴨脖稍微會比上海做的辣 /// </summary> public class NanChangYaBo : YaBo { public override void Print() { Console.WriteLine("南昌的鴨脖"); } } /// <summary> /// 上海的鴨脖沒有南昌的鴨脖做的辣 /// </summary> public class ShangHaiYaBo : YaBo { public override void Print() { Console.WriteLine("上海的鴨脖"); } } /// <summary> /// 南昌的鴨架 /// </summary> public class NanChangYaJia : YaJia { public override void Print() { Console.WriteLine("南昌的鴨架子"); } } /// <summary> /// 上海的鴨架 /// </summary> public class ShangHaiYaJia : YaJia { public override void Print() { Console.WriteLine("上海的鴨架子"); } } |
2.2 抽象工廠模式的定義和類圖
上面程式碼中都有詳細的註釋,這裡就不再解釋上面的程式碼了,下面就具體看看抽象工廠模式的定義吧(理解定義可以參考上面的實現來加深理解):
抽象工廠模式:提供一個建立產品的介面來負責建立相關或依賴的物件,而不具體明確指定具體類
抽象工廠允許客戶使用抽象的介面來建立一組相關產品,而不需要知道或關心實際生產出的具體產品是什麼。這樣客戶就可以從具體產品中被解耦。下面通過抽象工模式的類圖來了解各個類中之間的關係:
2.3 抽象工廠應對需求變更
看完上面抽象工廠的實現之後,如果 “絕味”公司又想在湖南開一家分店怎麼辦呢? 因為湖南人喜歡吃麻辣的,下面就具體看看應用了抽象工廠模式的系統是如何應對這種需求的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/// <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反編譯工具檢視該類的實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/// 扮演抽象工廠的角色 /// 建立連線資料庫時所需要的物件集合, /// 這個物件集合包括有 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資料的具體工廠實現,具體程式碼可以用反編譯工具檢視,具體程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/// 扮演著具體工廠的角色,用來建立連線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的具體工廠的實現。
五、總結
到這裡,抽象工廠模式的介紹就結束,在下一專題就將為大家介紹建造模式。
程式原始碼:抽象工廠模式實現