抽象工廠模式(Abstract Factory),提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類。javascript
如下給出抽象工廠方法模式的UML圖:html
回到《大話設計模式》裏面的雙數據庫訪問的例子:java
namespace ConsoleApplication1 { class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _name; public string Name { get { return _name; } set { _name = value; } } } class Department { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _deptNmae { get { return _deptNmae; } set { _deptNmae = value; } } } //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合 interface IUser { void Insert(User user); User GetUser(int id); } class SqlserverUser : IUser { public void Insert(User user) { Console.WriteLine("Sql Server添加一條記錄"); } public User GetUser(int id) { Console.WriteLine("Sql Server根據ID獲得User表的一條記錄"); return null; } } class AccessUser : IUser { public void Insert(User user) { Console.WriteLine("在Access中給User表增長一條記錄"); } public User GetUser(int id) { Console.WriteLine("在Access中根據ID獲得User表的一條記錄"); return null; } } interface IDepartment { void Insert(Department department); Department GetDepartment(int id); } class SqlserverDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Sqlserver中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄"); return null; } } class AccessDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Access中根據ID獲得Department表的一條記錄"); return null; } } interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); } //SQLServer實例化工廠,負責實例化SqlserverUser和SqlserverDepartment class SqlServerFactory : IFactory { public IUser CreateUser() { return new SqlserverUser(); } public IDepartment CreateDepartment() { return new SqlserverDepartment(); } } //Access實例化工廠,負責實例化AccessUser和AccessDepartment class AccessFactory : IFactory { public IUser CreateUser() { return new AccessUser(); } public IDepartment CreateDepartment() { return new AccessDepartment(); } } class Program { static void Main(string[] args) { IFactory factory = new SqlServerFactory(); IUser iu = factory.CreateUser(); //得到數據庫User訪問類的對象 iu.Insert(new User()); iu.GetUser(1); IDepartment idepartment = factory.CreateDepartment(); //得到數據庫Department訪問類的對象 idepartment.Insert(new Department()); idepartment.GetDepartment(1); Console.ReadKey(); } } }
其實以上代碼就叫作抽象工廠模式了,當只有一個User表的時候,就叫工廠方法模式。當有了多個表了,就叫抽象工廠模式。可是以上代碼是很是很差的,爲何?例如當你還要增長一個Project表的時候,就須要增長三個類,IProject、SqlserverProject、AccessProject,同時還須要更改IFactory、SqlserverFactory、AccessFactory。增長一張表就要更改三個類,這個是比較悲劇的。git
所以,如下給出一個用簡單工廠模式來優化抽象工廠模式的代碼:數據庫
namespace ConsoleApplication1 { class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _name; public string Name { get { return _name; } set { _name = value; } } } class Department { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _deptNmae { get { return _deptNmae; } set { _deptNmae = value; } } } //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合 interface IUser { void Insert(User user); User GetUser(int id); } class SqlserverUser : IUser { public void Insert(User user) { Console.WriteLine("Sql Server添加一條記錄"); } public User GetUser(int id) { Console.WriteLine("Sql Server根據ID獲得User表的一條記錄"); return null; } } class AccessUser : IUser { public void Insert(User user) { Console.WriteLine("在Access中給User表增長一條記錄"); } public User GetUser(int id) { Console.WriteLine("在Access中根據ID獲得User表的一條記錄"); return null; } } interface IDepartment { void Insert(Department department); Department GetDepartment(int id); } class SqlserverDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Sqlserver中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄"); return null; } } class AccessDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Access中根據ID獲得Department表的一條記錄"); return null; } } class DataAccess { private static readonly string db = "SqlServer"; //事先就設置好的數據庫 //private static readonly string db = "Access"; //也就是說,更改數據庫改的是這裏了 public static IUser CreateUser() //這是第一個User的簡單工廠 { IUser result = null; switch (db) //根據db的設置實例化不一樣的User數據庫訪問對象 { case "SqlServer": result = new SqlserverUser(); break; case "Access": result = new AccessUser(); break; } return result; } public static IDepartment CreateDepartment() //這是Department的簡單工廠 { IDepartment result = null; switch (db) { case "Sqlserver": result = new SqlserverDepartment(); break; case "Access": result = new AccessDepartment(); break; } return result; } } class Program { static void Main(string[] args) { IUser iu = DataAccess.CreateUser(); //得到數據庫User訪問類的對象,要改數據庫就要改DataAccess裏面的設置 iu.Insert(new User()); iu.GetUser(1); IDepartment idepartment = DataAccess.CreateDepartment(); //得到數據庫Department訪問類的對象,這裏沒有依賴SqlserverFactory或AccessFactory了 idepartment.Insert(new Department()); idepartment.GetDepartment(1); Console.ReadKey(); } } }
通過簡單工廠模式優化以後,客戶端就再也不依賴於SqlserverFactory或AccessFactory了,帶到了解耦的目的。若是要添加一個Project,只須要添加響應類,但改就只須要修改DataAccess就能夠了(在DataAccess裏面在添加一個簡單工廠)。很明顯者不是最終方案,由於簡單工廠方法仍是要修改,switch case。設計模式
如下還有經過反射實現的更加好的方案:app
namespace ConsoleApplication1 { class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _name; public string Name { get { return _name; } set { _name = value; } } } class Department { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _deptNmae { get { return _deptNmae; } set { _deptNmae = value; } } } //IUser接口,用於客戶端訪問,解除與具體數據庫訪問的耦合 interface IUser { void Insert(User user); User GetUser(int id); } class SqlserverUser : IUser { public void Insert(User user) { Console.WriteLine("Sql Server添加一條記錄"); } public User GetUser(int id) { Console.WriteLine("Sql Server根據ID獲得User表的一條記錄"); return null; } } class AccessUser : IUser { public void Insert(User user) { Console.WriteLine("在Access中給User表增長一條記錄"); } public User GetUser(int id) { Console.WriteLine("在Access中根據ID獲得User表的一條記錄"); return null; } } interface IDepartment { void Insert(Department department); Department GetDepartment(int id); } class SqlserverDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Sqlserver中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Sqlserver中根據ID獲得Department表的一條記錄"); return null; } } class AccessDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中給Department表插入一條記錄"); } public Department GetDepartment(int id) { Console.WriteLine("在Access中根據ID獲得Department表的一條記錄"); return null; } } class DataAccess { private static readonly string AssemblyName = "ConsoleApplication1"; //字符串爲程序集的名字 private static readonly string db = ConfigurationManager.AppSettings["DB"]; public static IUser CreateUser() { string className = AssemblyName + "." + db +"User"; //拼接SqlserverUser類或AccessUser類所在的位置 return (IUser)Assembly.Load(AssemblyName).CreateInstance(className); //反射建立實例 } public static IDepartment CreateDepartment() { string className = AssemblyName + "." + "SqlserverDepartment"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); } } class Program { static void Main(string[] args) { IUser iu = DataAccess.CreateUser(); //得到數據庫User訪問類的對象,要改數據庫就要改DataAccess裏面的設置 iu.Insert(new User()); iu.GetUser(1); IDepartment idepartment = DataAccess.CreateDepartment(); //得到數據庫Department訪問類的對象 idepartment.Insert(new Department()); idepartment.GetDepartment(1); Console.ReadKey(); } } }
配置文件App.config代碼:ide
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="DB" value="Access" /> </appSettings> </configuration>
以上就是反射+抽象工廠的方法, 仍是很是強大的,強大到什麼程度呢?例如上面的代碼,要修改數據庫訪問對象例如將Sqlserver改成Access根本不須要改動程序從新編譯,只須要改動App.config就能夠了。其實這裏的反射只是改進了簡單工廠模式而已,與抽象工廠沒太大的關係。要記住,全部在用到簡單工廠的地方均可以考慮用反射技術來去除switch解除分支判斷所帶來的耦合。post
在以上的代碼中,要增長一個Project表的話,只須要添加三個Project相關的類(擴展),再修改DataAccess,在其中增長一個CreateProject()方法就能夠了。學習