设计模式学习笔记六:.NET反射工厂
? 1. 簡述
????通過前面的學習,我們以傳統的方式實現了簡單工廠,工廠方法和抽象工廠,但是有些場合下如此處理,代碼會變得冗余并且難以維護。假設我們要創建交通工具。可以是汽車,火車,輪船等,其結構如下:
?????我們可以采用簡單工廠,通過參數指示創建所需要的對象類型。如果要增加子類,例如卡車和轎車,則必須增加參數和相應的代碼。如果子類層次過多,則會是程序變得很難維護。?
????但我們可以采用工廠方法模式來實現,即定義一個產生交通工具的接口,然后在子類中實現創建具體子類。代碼如下:
Code
public?interface?ICreateVehicle
????{
?????????Vehicle?CreateCehicle();
????} Code
?public?abstract?class?Vehicle
????{
????} Code
????public?class?Car:Vehicle
????{
????????public?Car()
????????{
????????????Console.WriteLine("創建了一個Car");
????????}
????} Code
????public?class?Boat:Vehicle?
????{
????????public?Boat()
????????{
????????????Console.WriteLine("創建了一個Boat");
????????}
????} Code
????public?class?CreateCar:ICreateVehicle
????{
????????ICreateVehicle?成員#region?ICreateVehicle?成員
????????public??Vehicle?CreateCehicle()
????????{
????????????Vehicle?vehicle=new?Car();
????????????return?vehicle;
????????}
????????#endregion
????} Code
????public?class?CreateBoat:ICreateVehicle
????{
????????ICreateVehicle?成員#region?ICreateVehicle?成員
????????public?Vehicle?CreateCehicle()
????????{
????????????Vehicle?vehicle?=?new?Boat();
????????????Console.WriteLine("Car");
????????????return?vehicle;
????????}
????????#endregion
????}
這就是工廠方法。如果希望增加新的交通工具,不僅需要實現交通工具接口,還需要實現生產交通工具的工廠方法。
????顯然我們需要幾十種交通工具,則需要幾十個具體的工廠。而這些類的區別僅僅是返回相對應的類的實例,所以位維護帶來了很大的麻煩。如果需要在接口中增加一個帶參數的方法,則所有的子類都需要修改。
在這種場合下,采用抽象工廠與工廠方法沒有區別,因為這里并不涉及產品線,抽象工廠并不能解決其中的問題,如果每種交通工具都要有對應的車站,則要使用抽象工廠,但是將會跟復雜。
有沒有可能將需要創建類的類型傳遞到工廠方法中,由工廠方法根據類型返回相應的實例?解決這個問題的關鍵是需要動態的決定需要創建的類,這不是設計模式能解決的問題,屬于軟件平臺的功能范疇。.NET可以提供反射技術。
我們先看通過反射技術實現的簡化的工廠,代碼如下:
?public?class?CreateVehicleByType:ICreateVehicle
????{
????????ICreateVehicle?成員#region?ICreateVehicle?成員
????????private?Type?VehicleTYpe;
????????public?CreateVehicleByType(string?strType)
????????{
????????????Type?t?=?Type.GetType(strType);
????????????VehicleTYpe?=?t;
????????}
????????public??Vehicle?CreateCehicle()
????????{
????????????ConstructorInfo??objConstrutor?=?VehicleTYpe.GetConstructor(System.Type.EmptyTypes);
????????????Vehicle?c?=?(Vehicle)objConstrutor.Invoke(null);
????????????return?c;
????????}
????????#endregion
???????
????}
在使用是,只要在創建時帶入需要創建的類的類型:
static?void?Main(string[]?args)
????????{
????????????string?strType?=?"Car";
????????????Vehicle?v;
????????????ICreateVehicle?f?=?null;
????????????if?(strType?==?"Car")
????????????{
????????????????f?=?new?CreateVehicleByType("FactoryVehicle.Car");
????????????}
????????????else?if?(strType?==?"Boat")
????????????{
????????????????f?=?new?CreateVehicleByType("Boat");
????????????}
????????????v?=?f.CreateCehicle();
????????????Console.ReadLine();
????????}
通過反射技術,我們將很多的具體的工廠類簡化為一個類,并且新增加類型時不需要新的工廠類,這樣我們得到簡化的工廠,可以稱其為“反射工廠”。
2.實例
???????? 先來看看,大話設計模式中的利用反射加抽象工廠的數據訪問程序。先來看看反射技術的基本格式:
Assembly.Load(“程序集名稱”).CreateInstance(“命名空間.類名稱”);
只要在程序頂端寫上using System.Reflection來引用Reflection,就可以采用反射工廠來克服抽象工廠模式的先天不足。下面我們來看通過反射技術實現不同數據庫的訪問程序.
???????? 先來看結構圖:
?
DataAccess類,用反射技術,取代了抽象工廠中的IFactory,SqlServerFactory和AccessFactory。
????具體代碼:
????
public?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;?}
????????}
????}
????public?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;?}
????????}
????}
????public?interface?IUser
????{
????????void?Insert(User?user);
????????User?GetUser(int?id);
????}
????public?class?SqlserverUser?:?IUser
????{
????????public?void?Insert(User?user)
????????{
????????????Console.WriteLine("在Sqlserver中給User表增加一條記錄");
????????}
????????public?User?GetUser(int?id)
????????{
????????????Console.WriteLine("在Sqlserver中根據ID得到User表一條記錄");
????????????return?null;
????????}
????}
????public?class?AccessUser?:?IUser
????{
????????public?void?Insert(User?user)
????????{
????????????Console.WriteLine("在Access中給User表增加一條記錄");
????????}
????????public?User?GetUser(int?id)
????????{
????????????Console.WriteLine("在Access中根據ID得到User表一條記錄");
????????????return?null;
????????}
????}
????public?interface?IDepartment
????{
????????void?Insert(Department?department);
????????Department?GetDepartment(int?id);
????}
????public?class?SqlserverDepartment?:?IDepartment
????{
????????public?void?Insert(Department?department)
????????{
????????????Console.WriteLine("在Sqlserver中給Department表增加一條記錄");
????????}
????????public?Department?GetDepartment(int?id)
????????{
????????????Console.WriteLine("在Sqlserver中根據ID得到Department表一條記錄");
????????????return?null;
????????}
????}
????public?class?AccessDepartment?:?IDepartment
????{
????????public?void?Insert(Department?department)
????????{
????????????Console.WriteLine("在Access中給Department表增加一條記錄");
????????}
????????public?Department?GetDepartment(int?id)
????????{
????????????Console.WriteLine("在Access中根據ID得到Department表一條記錄");
????????????return?null;
????????}
????}
????public?class?DataAccess
????{
????????private?static?readonly?string?AssemblyName?=?"抽象工廠模式";
????????private?static?readonly?string?db?=?"Sqlserver";
????????//private?static?readonly?string?db?=?"Access";
????????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);
????????}
????}
調用代碼:
????????????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();
????????}
????現在我們要增加Oracle數據訪問,相關類的增加是不可避免的,這點是無論我們用什么方法都解決不了的,這是擴展,依照開發-封閉原則,對于擴展,我們開放,但對與修改我們關閉。就現在的代碼中,我們要換Oracle很容易,只需將db=”Sqlserver”換成db=”Oracle”。
????現在我們需要增加Product,只需增加三個與Product相關的類,再修改一下DataAccess,在其中增加一個創建Product的方法就可以了。
????現在我們要更換數據訪問程序是,我們還需要修改程序,重新編譯,我們可以利用配置文件來解決這個問題,首先要在我們的項目中添加config文件,內容如下:
<?xml?version="1.0"?encoding="utf-8"??>
<configuration>
????<appSettings>
????????<add?key="DB"?value="Sqlserver"/>
????</appSettings>
</configuration>
再在項目中引用
Code
class?DataAccess
????{
????????private?static?readonly?string?AssemblyName?=?"抽象工廠模式";
????????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);
????????}
????}
???????? 使用反射工廠的優點是極大的減少了工廠類的數量,降低了代碼的冗余,并且系統更容易擴展,增加新類型后,不需要修改工廠類。
???????? 使用反射工廠的代價是工廠與產品之間的依賴關系不明顯,由于動態綁定,因此理論上可以用一個工廠完成很多類型的實例化,從而使得代碼不容易理解。另外就是增加了測試難度,因為創建是動態完成的。
???????? 采用反射技術創建的反射工廠可以使系統更靈活,使工廠和產品之間的依賴關系更小。在.NET的項目中大量的使用了反射工廠取代的傳統的工廠。
總結
以上是生活随笔為你收集整理的设计模式学习笔记六:.NET反射工厂的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020-3-25
- 下一篇: asp.net如何取得纯客户端控件的值