1、引言
寫了3篇有關設計模式的文章了,你們有了些反饋,說能從中學到一些東西,我感到很欣慰,那就繼續努力。今天我要寫第四個模式了,該模式叫抽象工廠。上一篇文章咱們講了【工廠方法】模式,它是爲了解決【簡單工廠】模式所面對的問題,它的問題就是:若是咱們增長新的產品,工廠類的方法就要修改自己的代碼,增長產品越多,其邏輯越複雜,同時這樣的修改也是不符合【開放關閉原則OCP】,對修改代碼關閉,對增長代碼開放。爲了解決【簡單工廠】的問題,咱們引出了【工廠方法】模式,經過子類化工廠類,解決了工廠類責任的劃分,產品和相應的工廠一一對應,符合了OCP。若是咱們要設計一套房子,固然咱們知道房子是由房頂、地板、窗戶、房門組成的,別的組件暫時省略,先設計一套古典風格的房子,再建立一套現代風格的房子,再建立一套歐式風格的房子,這麼多套房子,咱們該怎麼辦呢?今天咱們要講的【抽象工廠】模式能夠很好的解決多套變化的問題。
2、抽象工廠詳細介紹數據庫
2.一、動機(Motivate):
在軟件系統中,常常面臨着"一系統相互依賴的對象"的建立工做:同時,因爲需求的變化,每每存在更多系列對象的建立工做。如何應對這種變化?如何繞過常規的對象建立方法(new),提供一種"封裝機制"來避免客戶程序和這種"多系列具體對象建立工做"的緊耦合?
2.二、意圖(Intent):
提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。 ——《設計模式》GoF設計模式
2.三、結構圖(Structure)
該圖是抽象工廠的UML圖,結合抽象工廠的意圖、動機和圖示來理解該模式,今天咱們就以建設房子爲例來講明抽象工廠的實現機理。
2.四、模式的組成
能夠看出,在抽象工廠模式的結構圖有如下角色:ide
(1)、抽象產品類角色(AbstractProduct):爲抽象工廠中相互依賴的每種產品定義抽象接口對象,也能夠這樣說,有幾種產品,就要聲明幾個抽象角色,每個抽象產品角色和一種具體的產品相匹配。
(2)、具體產品類(ConcreteProduct):具體產品類實現了抽象產品類,是針對某個具體產品的實現的類型。
(3)、抽象工廠類角色(Abstract Factory):定義了建立一組相互依賴的產品對象的接口操做,每種操做和每種產品一一對應。
(4)、具體工廠類角色(ConcreteFactory):實現抽象類裏面的全部抽象接口操做,能夠建立某系列具體的產品,這些具體的產品是「抽象產品類角色」的子類。工具
2.五、抽象工廠的具體代碼實現
隨着咱們年齡的增大,咱們也到告終婚的年齡。結婚首要的問題就是房子的問題,假設我有一個頗有錢的爸爸,哈哈,有錢能夠解決不少問題。做爲長子的我,但願能有一套歐式風格的房子,再加上田園風光,今生足矣。我弟弟就不同了,他想要一套現代樣式的房子,若是兄弟姊妹再多年一點,那就有更多的要求了。因爲房子由房頂、地板、窗戶和房門組成,其餘組件暫時省略,有這麼多套房子要建設,每套房子的房頂、地板、窗戶和房門都是一個體系的,那就讓咱們看看如何使用【抽象工廠】模式來實現不一樣房屋的建造。學習
1 /// <summary> 2 /// 下面以不一樣系列房屋的建造爲例子演示抽象工廠模式 3 /// 由於每一個人的喜愛不同,我喜歡歐式的,我弟弟就喜歡現代的 4 /// 客戶端調用 5 /// </summary> 6 class Client 7 { 8 static void Main(string[] args) 9 { 10 // 哥哥的歐式風格的房子 11 AbstractFactory europeanFactory= new EuropeanFactory(); 12 europeanFactory.CreateRoof().Create(); 13 europeanFactory.CreateFloor().Create(); 14 europeanFactory.CreateWindow().Create(); 15 europeanFactory.CreateDoor().Create(); 16 17 18 //弟弟的現代風格的房子 19 AbstractFactory modernizationFactory = new ModernizationFactory(); 20 modernizationFactory.CreateRoof().Create(); 21 modernizationFactory.CreateFloor().Create(); 22 modernizationFactory.CreateWindow().Create(); 23 modernizationFactory.CreateDoor().Create(); 24 Console.Read(); 25 } 26 } 27 28 /// <summary> 29 /// 抽象工廠類,提供建立不一樣類型房子的接口 30 /// </summary> 31 public abstract class AbstractFactory 32 { 33 // 抽象工廠提供建立一系列產品的接口,這裏做爲例子,只給出了房頂、地板、窗戶和房門建立接口 34 public abstract Roof CreateRoof(); 35 public abstract Floor CreateFloor(); 36 public abstract Window CreateWindow(); 37 public abstract Door CreateDoor(); 38 } 39 40 /// <summary> 41 /// 歐式風格房子的工廠,負責建立歐式風格的房子 42 /// </summary> 43 public class EuropeanFactory : AbstractFactory 44 { 45 // 製做歐式房頂 46 public override Roof CreateRoof() 47 { 48 return new EuropeanRoof(); 49 } 50 51 // 製做歐式地板 52 public override Floor CreateFloor() 53 { 54 return new EuropeanFloor(); 55 } 56 57 // 製做歐式窗戶 58 public override Window CreateWindow() 59 { 60 return new EuropeanWindow(); 61 } 62 63 // 製做歐式房門 64 public override Door CreateDoor() 65 { 66 return new EuropeanDoor(); 67 } 68 } 69 70 /// <summary> 71 /// 如今風格房子的工廠,負責建立現代風格的房子 72 /// </summary> 73 public class ModernizationFactory : AbstractFactory 74 { 75 // 製做現代房頂 76 public override Roof CreateRoof() 77 { 78 return new ModernizationRoof(); 79 } 80 81 // 製做現代地板 82 public override Floor CreateFloor() 83 { 84 return new ModernizationFloor(); 85 } 86 87 // 製做現代窗戶 88 public override Window CreateWindow() 89 { 90 return new ModernizationWindow(); 91 } 92 93 // 製做現代房門 94 public override Door CreateDoor() 95 { 96 return new ModernizationDoor(); 97 } 98 } 99 100 /// <summary> 101 /// 房頂抽象類,子類的房頂必須繼承該類 102 /// </summary> 103 public abstract class Roof 104 { 105 /// <summary> 106 /// 建立房頂 107 /// </summary> 108 public abstract void Create(); 109 } 110 111 /// <summary> 112 /// 地板抽象類,子類的地板必須繼承該類 113 /// </summary> 114 public abstract class Floor 115 { 116 /// <summary> 117 /// 建立地板 118 /// </summary> 119 public abstract void Create(); 120 } 121 122 /// <summary> 123 /// 窗戶抽象類,子類的窗戶必須繼承該類 124 /// </summary> 125 public abstract class Window 126 { 127 /// <summary> 128 /// 建立窗戶 129 /// </summary> 130 public abstract void Create(); 131 } 132 133 /// <summary> 134 /// 房門抽象類,子類的房門必須繼承該類 135 /// </summary> 136 public abstract class Door 137 { 138 /// <summary> 139 /// 建立房門 140 /// </summary> 141 public abstract void Create(); 142 } 143 144 /// <summary> 145 /// 歐式地板類 146 /// </summary> 147 public class EuropeanFloor : Floor 148 { 149 public override void Create() 150 { 151 Console.WriteLine("建立歐式的地板"); 152 } 153 } 154 155 156 /// <summary> 157 /// 歐式的房頂 158 /// </summary> 159 public class EuropeanRoof : Roof 160 { 161 public override void Create() 162 { 163 Console.WriteLine("建立歐式的房頂"); 164 } 165 } 166 167 168 /// <summary> 169 ///歐式的窗戶 170 /// </summary> 171 public class EuropeanWindow : Window 172 { 173 public override void Create() 174 { 175 Console.WriteLine("建立歐式的窗戶"); 176 } 177 } 178 179 180 /// <summary> 181 /// 歐式的房門 182 /// </summary> 183 public class EuropeanDoor : Door 184 { 185 public override void Create() 186 { 187 Console.WriteLine("建立歐式的房門"); 188 } 189 } 190 191 /// <summary> 192 /// 現代的房頂 193 /// </summary> 194 public class ModernizationRoof : Roof 195 { 196 public override void Create() 197 { 198 Console.WriteLine("建立現代的房頂"); 199 } 200 } 201 202 /// <summary> 203 /// 現代的地板 204 /// </summary> 205 public class ModernizationFloor : Floor 206 { 207 public override void Create() 208 { 209 Console.WriteLine("建立現代的地板"); 210 } 211 } 212 213 /// <summary> 214 /// 現代的窗戶 215 /// </summary> 216 public class ModernizationWindow : Window 217 { 218 public override void Create() 219 { 220 Console.WriteLine("建立現代的窗戶"); 221 } 222 } 223 224 /// <summary> 225 /// 現代的房門 226 /// </summary> 227 public class ModernizationDoor : Door 228 { 229 public override void Create() 230 { 231 Console.WriteLine("建立現代的房門"); 232 } 233 }
2.六、 抽象工廠應對需求變動
讓咱們看看該模式如何應對需求的變化,假設個人表弟一看咱們的房子很好,他也想要一套古典風格的房子(哈哈,這個傢伙事挺多的,有好事老是落不下他)。
ui
1 /// <summary> 2 ///先爲表弟的房子來創建一個工廠類吧 3 /// </summary> 4 public class ClassicalFactory : AbstractFactory 5 { 6 //建立房頂 7 public override Roof CreateRoof() 8 { 9 return new ClassicalRoof(); 10 } 11 12 // 建立地板 13 public override Floor CreateFloor() 14 { 15 return new ClassicalFloor(); 16 } 17 18 // 建立窗戶 19 public override Window CreateWindow() 20 { 21 return new ClassicalWindow(); 22 } 23 24 // 建立房門 25 public override Door CreateDoor() 26 { 27 return new ClassicalDoor(); 28 } 29 } 30 31 /// <summary> 32 ///古典的房頂 33 /// </summary> 34 public class ClassicalRoof : Roof 35 { 36 public override void Create() 37 { 38 Console.WriteLine("建立古典的房頂"); 39 } 40 } 41 42 /// <summary> 43 /// 古典的地板 44 /// </summary> 45 public class ClassicalFloor : Floor 46 { 47 public override void Create() 48 { 49 Console.WriteLine("建立古典的地板"); 50 } 51 } 52 53 /// <summary> 54 /// 古典的窗戶 55 /// </summary> 56 public class ClassicalWindow : Window 57 { 58 public override void Create() 59 { 60 Console.WriteLine("建立古典的窗戶"); 61 } 62 } 63 64 /// <summary> 65 /// 古典的房門 66 /// </summary> 67 public class ClassicalDoor: Door 68 { 69 public override void Create() 70 { 71 Console.WriteLine("建立古典的房門"); 72 } 73 }
此時,只須要添加五個類:一個是古典風格工廠類,負責建立古典風格的房子,另外幾個類是具備古典風格的房頂、地板、窗戶和房門的具體產品。從上面代碼看出,抽象工廠對於系列產品的變化支持 「開放——封閉」原則(指的是要求系統對擴展開放,對修改封閉),擴展起來很是簡便,可是,抽象工廠對於增長新產品這種狀況就不支持」開放——封閉 「原則,由於要修改建立系列產品的抽象基類AbstractFactory,增長相應產品的建立方法,這也是抽象工廠的缺點所在。
3、抽象工廠的實現要點
一、若是沒有應對「多系列對象建立」的需求變化,則沒有必要使用AbstractFactory模式,這時候使用簡單的靜態工廠徹底能夠。
二、"系列對象"指的是這些對象之間有相互依賴、或做用的關係,例如遊戲開發場景中「道路」與「房屋」的依賴,「道路」與「地道」的依賴。
三、AbstractFactory模式主要在於應對「新系列」的需求變更。其缺點在於難以應對「新對象」的需求變更。
四、AbstractFactory模式常常喝FactoryMethod模式共同組合來應對「對象建立」的需求變化。
3.1】、抽象工廠模式的優勢:【抽象工廠】模式將系列產品的建立工做延遲到具體工廠的子類中,咱們聲明工廠類變量的時候是使用的抽象類型,同理,咱們使用產品類型也是抽象類型,這樣作就儘量的能夠減小客戶端代碼與具體產品類之間的依賴,從而下降了系統的耦合度。耦合度下降了,對於後期的維護和擴展就更有利,這也就是【抽象工廠】模式的優勢所在。可能有人會說在Main方法裏面(這裏的代碼就是客戶端的使用方)仍是會使用具體的工廠類,對的。這個其實咱們經過Net的配置,把這部分移出去,最後把依賴關係放到配置文件中。若是有新的需求咱們只須要修改配置文件,根本就不須要修改代碼了,讓客戶代碼更穩定。依賴關係確定會存在,咱們要作的就是下降依賴,想徹底去除很難,也不現實。
3.2】、抽象工廠模式的缺點:有優勢確定就有缺點,由於每種模式都有他的使用範圍,或者說要解決的問題,不能解決的問題就是缺點了,其實也不能叫缺點了。【抽象工廠】模式很難支持增長新產品的變化,這是由於抽象工廠接口中已經肯定了能夠被建立的產品集合,若是須要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及全部子類的改變,這樣也就違背了「開發——封閉」原則。
3.3】、抽象工廠模式的使用場景: 若是系統須要多套的代碼解決方案,而且每套的代碼方案中又有不少相互關聯的產品類型,而且在系統中咱們能夠相互替換的使用一套產品的時候可使用該模式,客戶端不須要依賴具體實現。
4、.NET中抽象工廠模式實現
微軟的類庫發展了這麼多年,設計模式在裏面有大量的應用,【抽象工廠】模式在.NET類庫中也存在着大量的使用,好比和操做數據庫有關的類型,這個類就是System.Data.Common.DbProviderFactory,這個類位於System.Data.dll程序集中。該類扮演抽象工廠模式中抽象工廠的角色,咱們能夠用ILSpy反編譯工具查看該類的實現:
/// 扮演抽象工廠的角色
/// 建立鏈接數據庫時所須要的對象集合,
/// 這個對象集合包括有 DbConnection對象(這個是抽象產品類,如絕味例子中的YaBo類)、DbCommand類、DbDataAdapter類,針對不一樣的具體工廠都須要實現該抽象類中方法,spa
1 public abstract class DbProviderFactory 2 { 3 public virtual bool CanCreateDataSourceEnumerator 4 { 5 get 6 { 7 return false; 8 } 9 } 10 11 public virtual DbCommand CreateCommand() 12 { 13 return null; 14 } 15 16 public virtual DbCommandBuilder CreateCommandBuilder() 17 { 18 return null; 19 } 20 21 public virtual DbConnection CreateConnection() 22 { 23 return null; 24 } 25 26 public virtual DbConnectionStringBuilder CreateConnectionStringBuilder() 27 { 28 return null; 29 } 30 31 public virtual DbDataAdapter CreateDataAdapter() 32 { 33 return null; 34 } 35 36 public virtual DbParameter CreateParameter() 37 { 38 return null; 39 } 40 41 public virtual CodeAccessPermission CreatePermission(PermissionState state) 42 { 43 return null; 44 } 45 46 public virtual DbDataSourceEnumerator CreateDataSourceEnumerator() 47 { 48 return null; 49 } 50 } 51 }
DbProviderFactory類是一個抽象工廠類,該類提供了建立數據庫鏈接時所須要的對象集合的接口,實際建立的工做在其子類工廠中進行,微軟使用的是SQL Server數據庫,所以提供了鏈接SQL Server數據的具體工廠實現,具體代碼能夠用反編譯工具查看,具體代碼以下:
SqlClientFactory扮演着具體工廠的角色,用來建立鏈接SQL Server數據所須要的對象設計
1 public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider 2 { 3 public static readonly SqlClientFactory Instance = new SqlClientFactory(); 4 5 public override bool CanCreateDataSourceEnumerator 6 { 7 get 8 { 9 return true; 10 } 11 } 12 13 private SqlClientFactory() 14 { 15 } 16 17 public override DbCommand CreateCommand() 18 { 19 return new SqlCommand(); 20 } 21 22 public override DbCommandBuilder CreateCommandBuilder() 23 { 24 return new SqlCommandBuilder(); 25 } 26 27 public override DbConnection CreateConnection() 28 { 29 return new SqlConnection(); 30 } 31 32 public override DbConnectionStringBuilder CreateConnectionStringBuilder() 33 { 34 return new SqlConnectionStringBuilder(); 35 } 36 37 public override DbDataAdapter CreateDataAdapter() 38 { 39 return new SqlDataAdapter(); 40 } 41 42 public override DbParameter CreateParameter() 43 { 44 return new SqlParameter(); 45 } 46 47 public override CodeAccessPermission CreatePermission(PermissionState state) 48 { 49 return new SqlClientPermission(state); 50 } 51 52 public override DbDataSourceEnumerator CreateDataSourceEnumerator() 53 { 54 return SqlDataSourceEnumerator.Instance; 55 } 56 57 object IServiceProvider.GetService(Type serviceType) 58 { 59 object result = null; 60 if (serviceType == GreenMethods.SystemDataCommonDbProviderServices_Type) 61 { 62 result = GreenMethods.SystemDataSqlClientSqlProviderServices_Instance(); 63 } 64 return result; 65 } 66 }
OdbcFactory也是具體工廠類code
1 public sealed class OdbcFactory : DbProviderFactory 2 { 3 public static readonly OdbcFactory Instance = new OdbcFactory(); 4 5 private OdbcFactory() 6 { 7 } 8 9 public override DbCommand CreateCommand() 10 { 11 return new OdbcCommand(); 12 } 13 14 public override DbCommandBuilder CreateCommandBuilder() 15 { 16 return new OdbcCommandBuilder(); 17 } 18 19 public override DbConnection CreateConnection() 20 { 21 return new OdbcConnection(); 22 } 23 24 public override DbConnectionStringBuilder CreateConnectionStringBuilder() 25 { 26 return new OdbcConnectionStringBuilder(); 27 } 28 29 public override DbDataAdapter CreateDataAdapter() 30 { 31 return new OdbcDataAdapter(); 32 } 33 34 public override DbParameter CreateParameter() 35 { 36 return new OdbcParameter(); 37 } 38 39 public override CodeAccessPermission CreatePermission(PermissionState state) 40 { 41 return new OdbcPermission(state); 42 } 43 }
固然,咱們也有OleDbFactory 類型,都是負責具體的數據庫操做。DbProviderFactory就是【抽象工廠】模式UML裏面AbstractFactory類型。其餘具體的工廠類型繼承DbProviderFactory類型,這個結構很簡單,我就不畫圖了。
5、總結
終於寫完了,寫了3個小時,學習設計模式不能死學,要把握核心點和使用場景。關鍵點第一是,面向對象設計模式的基本原則,有了原則,考慮問題就不會跑偏,而後再仔細把握每種模式的使用場景和要解決的問題,多寫寫代碼,多看看Net的類庫,它是最好的教材。面向對象設計模式