本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,記錄一下學習過程以備後續查用。
一、引言
接上一篇C#設計模式學習筆記:簡單工廠模式(工廠方法模式前奏篇),通過簡單工廠模式的瞭解,它的缺點就是隨着需求的變化我們要不停地修改工廠裏
上一篇文章我們講了工廠方法模式,它是爲了解決簡單工廠模式所面對的問題:如果我們增加新的產品,工廠類的方法就要修改本身的代碼,增加產品越
多,其邏輯越複雜,同時這樣的修改也不符合開放閉合原則OCP--對增加代碼開放,對修改代碼關閉。爲了解決簡單工廠的問題,我們引出了工廠方法模式,
通過子類化工廠類,解決了工廠類責任的劃分,使得產品和相應的工廠一一對應,符合了OCP。
如果我們要設計一套房子,當然我們知道房子是由房頂、地板、窗戶、房門等組成的,先設計一套古典風格的房子,再創建一套現代風格的房子,再創建
一套歐式風格的房子,這麼多套房子,我們該怎麼辦呢?今天我們要講的抽象工廠模式可以很好地解決多套變化的問題。
二、抽象工廠模式介紹
抽象工廠模式:英文名稱--Abstract Factory Pattern;分類--創建型。
2.1、動機(Motivate)
在軟件系統中,經常面臨着"一系列相互依賴的對象"的創建工作,同時,由於需求的變化,往往存在更多系列對象的創建工作。如何應對這種變化?如何
繞過常規的對象創建方法(new),提供一種"封裝機制"來避免客戶程序和這種"多系列具體對象創建工作"的緊耦合?
2.2、意圖(Intent)
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。——《設計模式》GoF
2.3、結構圖(Structure)
該圖是抽象工廠的UML圖,結合抽象工廠的意圖、動機和圖示來理解該模式,今天我們就以建設房子爲例來說明抽象工廠的實現機理。
2.4、模式的組成
從上圖可以看出,在抽象工廠模式的結構圖有以下角色:
1)抽象產品類角色(AbstractProduct):爲抽象工廠中相互依賴的每種產品定義抽象接口對象,也可以這樣說,有幾種產品,就要聲明幾個抽象角色,
每一個抽象產品角色和一種具體的產品相匹配。
2)具體產品類(ConcreteProduct):具體產品類實現了抽象產品類,是針對某個具體產品的實現的類型。
3)抽象工廠類角色(AbstractFactory):定義了創建一組相互依賴的產品對象的接口操作,每種操作和每種產品一一對應。
4)具體工廠類角色(ConcreteFactory):實現抽象類裏所有抽象接口操作,可以創建某系列具體的產品,這些具體的產品是“抽象產品類角色”的子類。
2.5、抽象工廠模式的具體實現
隨着我們年齡的增長,我們也到了結婚的年齡,結婚首要的問題就是房子的問題。假設我有一個很有錢的爸爸(呃,發夢中……),我的哥哥們希望能有
一套歐式風格的房子,再加上田園風光,悠閒自在。而我就不一樣了,我希望有一套現代樣式的房子。由於房子由房頂、地板、窗戶和房門組成(其他組
件暫時省略),每套房子的房頂、地板、窗戶和房門都是一個體系的。
下面讓我們看看如何使用抽象工廠模式來實現不同房屋的建造:
class Program
{
/// <summary>
/// 房頂抽象類,子類的房頂必須繼承該類。
/// </summary>
public abstract class Roof
{
/// <summary>
/// 創建房頂
/// </summary>
public abstract void Create();
}
/// <summary>
/// 地板抽象類,子類的地板必須繼承該類。
/// </summary>
public abstract class Floor
{
/// <summary>
/// 創建地板
/// </summary>
public abstract void Create();
}
/// <summary>
/// 窗戶抽象類,子類的窗戶必須繼承該類。
/// </summary>
public abstract class Window
{
/// <summary>
/// 創建窗戶
/// </summary>
public abstract void Create();
}
/// <summary>
/// 房門抽象類,子類的房門必須繼承該類。
/// </summary>
public abstract class Door
{
/// <summary>
/// 創建房門
/// </summary>
public abstract void Create();
}
/// <summary>
/// 歐式的房頂
/// </summary>
public class EuropeanRoof : Roof
{
public override void Create()
{
Console.WriteLine("創建歐式的房頂");
}
}
/// <summary>
/// 歐式的地板
/// </summary>
public class EuropeanFloor : Floor
{
public override void Create()
{
Console.WriteLine("創建歐式的地板");
}
}
/// <summary>
///歐式的窗戶
/// </summary>
public class EuropeanWindow : Window
{
public override void Create()
{
Console.WriteLine("創建歐式的窗戶");
}
}
/// <summary>
/// 歐式的房門
/// </summary>
public class EuropeanDoor : Door
{
public override void Create()
{
Console.WriteLine("創建歐式的房門");
}
}
/// <summary>
/// 現代的房頂
/// </summary>
public class ModernizationRoof : Roof
{
public override void Create()
{
Console.WriteLine("創建現代的房頂");
}
}
/// <summary>
/// 現代的地板
/// </summary>
public class ModernizationFloor : Floor
{
public override void Create()
{
Console.WriteLine("創建現代的地板");
}
}
/// <summary>
/// 現代的窗戶
/// </summary>
public class ModernizationWindow : Window
{
public override void Create()
{
Console.WriteLine("創建現代的窗戶");
}
}
/// <summary>
/// 現代的房門
/// </summary>
public class ModernizationDoor : Door
{
public override void Create()
{
Console.WriteLine("創建現代的房門");
}
}
/// <summary>
/// 抽象工廠類,提供創建不同類型房子的接口。
/// </summary>
public abstract class AbstractFactory
{
//抽象工廠提供創建一系列產品的接口,此處給出了房頂、地板、窗戶和房門的創建接口。
public abstract Roof CreateRoof();
public abstract Floor CreateFloor();
public abstract Window CreateWindow();
public abstract Door CreateDoor();
}
/// <summary>
/// 歐式風格房子的工廠,負責創建歐式風格的房子。
/// </summary>
public class EuropeanFactory : AbstractFactory
{
//製作歐式房頂
public override Roof CreateRoof()
{
return new EuropeanRoof();
}
//製作歐式地板
public override Floor CreateFloor()
{
return new EuropeanFloor();
}
//製作歐式窗戶
public override Window CreateWindow()
{
return new EuropeanWindow();
}
//製作歐式房門
public override Door CreateDoor()
{
return new EuropeanDoor();
}
}
/// <summary>
/// 現在風格房子的工廠,負責創建現代風格的房子。
/// </summary>
public class ModernizationFactory : AbstractFactory
{
//製作現代房頂
public override Roof CreateRoof()
{
return new ModernizationRoof();
}
//製作現代地板
public override Floor CreateFloor()
{
return new ModernizationFloor();
}
//製作現代窗戶
public override Window CreateWindow()
{
return new ModernizationWindow();
}
//製作現代房門
public override Door CreateDoor()
{
return new ModernizationDoor();
}
}
static void Main(string[] args)
{
#region 抽象工廠模式
//歐式風格的房子
AbstractFactory europeanFactory = new EuropeanFactory();
europeanFactory.CreateRoof().Create();
europeanFactory.CreateFloor().Create();
europeanFactory.CreateWindow().Create();
europeanFactory.CreateDoor().Create();
//現代風格的房子
AbstractFactory modernizationFactory = new ModernizationFactory();
modernizationFactory.CreateRoof().Create();
modernizationFactory.CreateFloor().Create();
modernizationFactory.CreateWindow().Create();
modernizationFactory.CreateDoor().Create();
Console.Read();
#endregion
}
}
運行結果如下:
2.6、 抽象工廠模式應對需求變更
假設我的姐姐一看我們的房子很好,她希望有一套古典風格的房子,怎麼處理呢?
class Program
{
/// <summary>
/// 房頂抽象類,子類的房頂必須繼承該類。
/// </summary>
public abstract class Roof
{
/// <summary>
/// 創建房頂
/// </summary>
public abstract void Create();
}
/// <summary>
/// 地板抽象類,子類的地板必須繼承該類。
/// </summary>
public abstract class Floor
{
/// <summary>
/// 創建地板
/// </summary>
public abstract void Create();
}
/// <summary>
/// 窗戶抽象類,子類的窗戶必須繼承該類。
/// </summary>
public abstract class Window
{
/// <summary>
/// 創建窗戶
/// </summary>
public abstract void Create();
}
/// <summary>
/// 房門抽象類,子類的房門必須繼承該類。
/// </summary>
public abstract class Door
{
/// <summary>
/// 創建房門
/// </summary>
public abstract void Create();
}
/// <summary>
/// 歐式的房頂
/// </summary>
public class EuropeanRoof : Roof
{
public override void Create()
{
Console.WriteLine("創建歐式的房頂");
}
}
/// <summary>
/// 歐式的地板
/// </summary>
public class EuropeanFloor : Floor
{
public override void Create()
{
Console.WriteLine("創建歐式的地板");
}
}
/// <summary>
///歐式的窗戶
/// </summary>
public class EuropeanWindow : Window
{
public override void Create()
{
Console.WriteLine("創建歐式的窗戶");
}
}
/// <summary>
/// 歐式的房門
/// </summary>
public class EuropeanDoor : Door
{
public override void Create()
{
Console.WriteLine("創建歐式的房門");
}
}
/// <summary>
/// 現代的房頂
/// </summary>
public class ModernizationRoof : Roof
{
public override void Create()
{
Console.WriteLine("創建現代的房頂");
}
}
/// <summary>
/// 現代的地板
/// </summary>
public class ModernizationFloor : Floor
{
public override void Create()
{
Console.WriteLine("創建現代的地板");
}
}
/// <summary>
/// 現代的窗戶
/// </summary>
public class ModernizationWindow : Window
{
public override void Create()
{
Console.WriteLine("創建現代的窗戶");
}
}
/// <summary>
/// 現代的房門
/// </summary>
public class ModernizationDoor : Door
{
public override void Create()
{
Console.WriteLine("創建現代的房門");
}
}
/// <summary>
///古典的房頂
/// </summary>
public class ClassicalRoof : Roof
{
public override void Create()
{
Console.WriteLine("創建古典的房頂");
}
}
/// <summary>
/// 古典的地板
/// </summary>
public class ClassicalFloor : Floor
{
public override void Create()
{
Console.WriteLine("創建古典的地板");
}
}
/// <summary>
/// 古典的窗戶
/// </summary>
public class ClassicalWindow : Window
{
public override void Create()
{
Console.WriteLine("創建古典的窗戶");
}
}
/// <summary>
/// 古典的房門
/// </summary>
public class ClassicalDoor : Door
{
public override void Create()
{
Console.WriteLine("創建古典的房門");
}
}
/// <summary>
/// 抽象工廠類,提供創建不同類型房子的接口。
/// </summary>
public abstract class AbstractFactory
{
//抽象工廠提供創建一系列產品的接口,此處給出了房頂、地板、窗戶和房門的創建接口。
public abstract Roof CreateRoof();
public abstract Floor CreateFloor();
public abstract Window CreateWindow();
public abstract Door CreateDoor();
}
/// <summary>
/// 歐式風格房子的工廠,負責創建歐式風格的房子。
/// </summary>
public class EuropeanFactory : AbstractFactory
{
//製作歐式房頂
public override Roof CreateRoof()
{
return new EuropeanRoof();
}
//製作歐式地板
public override Floor CreateFloor()
{
return new EuropeanFloor();
}
//製作歐式窗戶
public override Window CreateWindow()
{
return new EuropeanWindow();
}
//製作歐式房門
public override Door CreateDoor()
{
return new EuropeanDoor();
}
}
/// <summary>
/// 現在風格房子的工廠,負責創建現代風格的房子。
/// </summary>
public class ModernizationFactory : AbstractFactory
{
//製作現代房頂
public override Roof CreateRoof()
{
return new ModernizationRoof();
}
//製作現代地板
public override Floor CreateFloor()
{
return new ModernizationFloor();
}
//製作現代窗戶
public override Window CreateWindow()
{
return new ModernizationWindow();
}
//製作現代房門
public override Door CreateDoor()
{
return new ModernizationDoor();
}
}
/// <summary>
/// 古典風格房子的工廠,負責創建古典風格的房子。
/// </summary>
public class ClassicalFactory : AbstractFactory
{
//創建古典房頂
public override Roof CreateRoof()
{
return new ClassicalRoof();
}
//創建古典地板
public override Floor CreateFloor()
{
return new ClassicalFloor();
}
//創建古典窗戶
public override Window CreateWindow()
{
return new ClassicalWindow();
}
//創建古典房門
public override Door CreateDoor()
{
return new ClassicalDoor();
}
}
static void Main(string[] args)
{
#region 抽象工廠模式
//歐式風格的房子
AbstractFactory europeanFactory = new EuropeanFactory();
europeanFactory.CreateRoof().Create();
europeanFactory.CreateFloor().Create();
europeanFactory.CreateWindow().Create();
europeanFactory.CreateDoor().Create();
//現代風格的房子
AbstractFactory modernizationFactory = new ModernizationFactory();
modernizationFactory.CreateRoof().Create();
modernizationFactory.CreateFloor().Create();
modernizationFactory.CreateWindow().Create();
modernizationFactory.CreateDoor().Create();
//古典風格的房子
AbstractFactory classicalFactory = new ClassicalFactory();
classicalFactory.CreateRoof().Create();
classicalFactory.CreateFloor().Create();
classicalFactory.CreateWindow().Create();
classicalFactory.CreateDoor().Create();
Console.Read();
#endregion
}
}
運行結果如下:
從上面代碼可以看出,需要添加5個類:4個類分別創建古典風格的房頂、地板、窗戶和房門的具體產品,另外一個是古典風格房子的工廠類,負責創建
古典風格的房子。
從上面代碼可以看出,抽象工廠對於系列產品的變化支持開閉原則(對擴展開放,對修改封閉),擴展起來非常簡便。但是,抽象工廠對於增加新產品
這種情況就不支持開閉原則,因爲要修改創建系列產品的抽象基類AbstractFactory,增加相應產品的創建方法,這也是抽象工廠的缺點所在。
三、抽象工廠模式的實現要點
1)如果沒有應對“多系列對象創建”的需求變化,則沒有必要使用AbstractFactory模式,這時候使用簡單工廠模式完全可以。
2)"系列對象"指的是這些對象之間有相互依賴、或作用的關係,例如遊戲開發場景中“道路”與“房屋”的依賴,“道路”與“地道”的依賴。
3)AbstractFactory模式主要在於應對“新系列”的需求變動,其缺點在於難以應對“新對象”的需求變動。
4)AbstractFactory模式經常和FactoryMethod模式共同組合來應對“對象創建”的需求變化。
3.1、抽象工廠模式的優點
抽象工廠模式將系列產品的創建工作延遲到具體工廠的子類中,我們聲明工廠類變量的時候使用的是抽象類型,同理,我們使用產品類型也是抽象類型,
這樣做可以儘可能地減少客戶端代碼與具體產品類之間的依賴,從而降低了系統的耦合度。耦合度降低了,對於後期的維護和擴展就更有利,這就是抽象
工廠模式的優點所在。
可能有人會說在Main方法裏面(客戶端)還是會使用具體的工廠類,對的。這個其實我們可以通過.Net配置文件把這部分移出去,把依賴關係放到配置文
件中。如果有新的需求我們只需要修改配置文件,根本就不需要修改代碼了,讓客戶代碼更穩定。依賴關係肯定會存在,我們要做的就是降低依賴,想完全
去除很難,也不現實。
3.2、抽象工廠模式的缺點
有優點肯定就有缺點,因爲每種模式都有它的使用範圍,或者說不能解決的問題就是缺點。抽象工廠模式很難支持增加新產品的變化,這是因爲抽象工廠
接口中已經確定了可以被創建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類以及所有子類的改變,這樣
也就違背了開閉原則。
3.3、抽象工廠模式的使用場景
如果系統需要多套的代碼解決方案,並且每套的代碼方案中又有很多相互關聯的產品類型,並且在系統中可以相互替換地使用一套產品的時候就可以使用
該模式,客戶端不需要依賴具體實現。
四、.NET中抽象工廠模式的實現
微軟的類庫發展了這麼多年,設計模式在裏面有大量的應用。抽象工廠模式在.NET類庫中也存在着大量的使用,比如和操作數據庫有關的類型,這個類是
System.Data.Common.DbProviderFactory,此類位於System.Data.dll程序集中。該類扮演抽象工廠模式中抽象工廠的角色,我們可以用ILSpy反編譯工具查
看該類的實現:
/// 扮演抽象工廠的角色
/// 創建連接數據庫時所需要的對象集合
/// 這個對象集合包括有DbConnection對象(抽象產品類)、DbCommand類、DbDataAdapter類,針對不同的具體工廠都需要實現該抽象類中的方法。
public abstract class DbProviderFactory
{
public virtual bool CanCreateDataSourceEnumerator
{
get
{
return false;
}
}
public virtual DbCommand CreateCommand()
{
return null;
}
public virtual DbCommandBuilder CreateCommandBuilder()
{
return null;
}
public virtual DbConnection CreateConnection()
{
return null;
}
public virtual DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return null;
}
public virtual DbDataAdapter CreateDataAdapter()
{
return null;
}
public virtual DbParameter CreateParameter()
{
return null;
}
public virtual CodeAccessPermission CreatePermission(PermissionState state)
{
return null;
}
public virtual DbDataSourceEnumerator CreateDataSourceEnumerator()
{
return null;
}
}
DbProviderFactory類是一個抽象工廠類,該類提供了創建數據庫連接時所需要的對象集合的接口,實際創建工作在其子類工廠中進行。微軟使用的是
SQL Server數據庫,因此提供了連接SQL Server數據的具體工廠實現,具體代碼可以用反編譯工具查看。
SqlClientFactory扮演着具體工廠的角色,用來創建連接SQL Server數據所需要的對象:
public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider
{
public static readonly SqlClientFactory Instance = new SqlClientFactory();
public override bool CanCreateDataSourceEnumerator
{
get
{
return true;
}
}
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 DbParameter CreateParameter()
{
return new SqlParameter();
}
public override CodeAccessPermission CreatePermission(PermissionState state)
{
return new SqlClientPermission(state);
}
public override DbDataSourceEnumerator CreateDataSourceEnumerator()
{
return SqlDataSourceEnumerator.Instance;
}
object IServiceProvider.GetService(Type serviceType)
{
object result = null;
if (serviceType == GreenMethods.SystemDataCommonDbProviderServices_Type)
{
result = GreenMethods.SystemDataSqlClientSqlProviderServices_Instance();
}
return result;
}
}
OdbcFactory也是具體工廠類:
public sealed class OdbcFactory : DbProviderFactory
{
public static readonly OdbcFactory Instance = new OdbcFactory();
private OdbcFactory()
{
}
public override DbCommand CreateCommand()
{
return new OdbcCommand();
}
public override DbCommandBuilder CreateCommandBuilder()
{
return new OdbcCommandBuilder();
}
public override DbConnection CreateConnection()
{
return new OdbcConnection();
}
public override DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return new OdbcConnectionStringBuilder();
}
public override DbDataAdapter CreateDataAdapter()
{
return new OdbcDataAdapter();
}
public override DbParameter CreateParameter()
{
return new OdbcParameter();
}
public override CodeAccessPermission CreatePermission(PermissionState state)
{
return new OdbcPermission(state);
}
}
當然,我們也有OleDbFactory類型,都是負責具體的數據庫操作。DbProviderFactory就是抽象工廠模式UML裏面AbstractFactory類型,其它具體的工廠類
型繼承於DbProviderFactory類型。
五、總結
學習設計模式不能死學,要把握核心點和使用場景,關鍵點是面向對象設計模式的基本原則。有了原則,考慮問題就不會跑偏,然後再仔細把握每種模式的
使用場景和要解決的問題,多寫寫代碼,多看看Net的類庫,它是最好的教材。