簡單工廠模式(不是GoF23種設計模式之一)、工廠方法模式和抽象工廠模式均屬於創建型設計模式,它們各有各的優缺點,相互之間有區別也有聯系,下面來看看三者之間的對比:
模式名稱
定義
優點
缺點
適用范圍
簡單工廠
由一個工廠類根據傳入的參數,動態決定應該創建哪一個產品類(這些產品類繼承自一個父類或接口)的實例。
工廠類中包含了必要的邏輯判斷,根據客戶端的選擇條件動態實例化相關的類,對於客戶端來說,去除了與具體產品的依賴。
每增加一個功能,都需要對工廠類的邏輯判斷進行修改。
工廠類負責創建的對象比較少,客戶只知道傳入了工廠類的參數,對於始何創建對象(邏輯)不關心。
工廠方法
定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
由於使用了多態性,工廠方法克服了簡單工廠違背開放-封閉原則的缺點,又保持了封裝對象創建過程的優點。
由於每增加一個產品,就需要加一個產品工廠的類,增加了額外的開發量。
當一個類不知道它所必須創建對象的類或一個類希望由子類來指定它所創建的對象時,當類將創建對象的職責委托給多個幫助子類中的某一個,並且你希望將哪一個幫助子類是代理者這一信息局部化的時候,可以使用工廠方法。
抽象工廠
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
1、易於交換產品系列。由於具體工廠類,在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。
2、它讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操縱實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中。
如果需求來自增加功能,此時,需要增加若干類,並修改相關的工廠類。
1. 一個系統要獨立於它的產品的創建、組合和表示時。
2.一個系統要由多個產品系列中的一個來配置時。
3.當你要強調一系列相關的產品對象的設計以便進行聯合時。
4.當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。
簡單工廠模式UML圖
工廠方法模式UML圖
3、抽象工廠模式UML圖
從上面UML圖的演變可以看出,最後的抽象工廠顯得非常臃腫。其中的一個解決方法就是將AbstractFactory、ConcreteFactory1和ConcreteFactory2合並為一個DataFactory,而且這三種模式有一種共同的缺點:不能避免分支判斷。為了解決這個問題,引入了反射(為了避免修改代碼,又引入了配置文件來實現數據訪問)這個方法。下面給出抽象工廠方法模式的相關代碼(實例參考大話設計模式第15章):
先給出解決方法的核心代碼:
[csharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection; //引入反射,必須要寫
using System.Configuration;
namespace 抽象工廠模式2用簡單工廠來改進抽象工廠
{
class DataAccess
{
#region 未使用反射
//private static readonly string db = "Sqlserver"; //數據庫名稱,可替換為Access
////private static readonly string db = "Access";
//public static IUser CreateUser()
//{
// IUser result = null;
// switch (db) //由於db的事先設置,所以此處可以根據選擇實例化出相應的對象
// {
// case "Sqlserver":
// result = new SqlserverUser();
// break;
// case "Access":
// result = new AccessUser();
// break;
// }
// return result;
//}
//public static IDepartment CreateDepartment()
//{
// IDepartment result = null;
// switch (db)
// {
// case "Sqlserver":
// result = new SqlserverDepartment();
// break;
// case "Access":
// result = new AccessDepartment();
// break;
// }
// return result;
//}
#endregion
private static readonly string AssemblyName = "抽象工廠模式2用簡單工廠來改進抽象工廠"; //AssemblyName就是本解決方案的名稱
//只使用反射
// private static readonly string db = "Sqlserver";
//private static readlonly string db="Access"; //數據庫名稱可以更換為Access
//使用反射+配置文件(使用前需要從引用中添加system.configuration)
private static readonly string db = ConfigurationManager.AppSettings["DB"]; //表示讀取配置文件
public static IUser CreateUser()
{
string className = AssemblyName + "." + db + "User";
return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
}
public static IDepartment CreateDepartment()
{
string className = AssemblyName + "." + db + "Department";
return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
}
}
}
然後是各個相關類的代碼:
[csharp]
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 _deptName;
public string DeptName
{
get { return _deptName; }
set { _deptName = value; }
}
}
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
class SqlserverUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在Sqlserver中給User表增加一條記錄");
}
public User GetUser(int id)
{
Console.WriteLine("在Sqlserver中根據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;
}
}
最後是客戶端代碼:
[csharp]
User user = new User();
Department dept = new Department();
IUser iu = DataAccess.CreateUser(); //直接得到實際的數據庫訪問實例,而不存在任何依賴
iu.Insert(user);
iu.GetUser(1);
IDepartment id = DataAccess.CreateDepartment(); //直接得到實際的數據庫訪問實例,而不存在任何依賴
id.Insert(dept);
id.GetDepartment(1);
Console.Read();
執行結果: