1.概述
如果采用傳統方式實現了簡單工廠、工廠方法和抽象工廠在有些場合下如此處理,代碼會變得冗余並且難以維護。
假設我們需要創建一種交通工具,可以是汽車、火車或者輪船,結構如圖所示。
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332144.jpg)
我們可以采用簡單工廠,通過參數指示創建所需要的對象類型。如果增加子類,例如卡車和轎車等,則必須增加參數的相應的代碼。如果子類層次很多,則會使程序變得難以維護如果用簡單工廠實現上面的結構,則顯然很煩瑣。
當然我們可以采用工廠方法來實現,即定義一個產生交通工具的接口,然後在子類中實現創建具體的子類。代碼如下:
采用接口定義了抽象的工廠方法
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
public Interface CreateVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Function CreateAVehicle() As Vehicle `創建一個交通工具
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Interface
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332237.gif)
` 具體的創建由子類決定
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
public Class CreateCar
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Implements CreateCar
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Function CreateAVheicle() AsVehicle Implements
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
CreateVehicle.CreateAVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return New Car
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Class
這就是工廠方法。如果我們希望增加一個新的交通工具,不僅需要實現工具接口,還需要實現產生交通工具的工廠方法。下面是船的具體工廠方法:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
Public Class CreateBoat
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Implements CreateVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Function CreateAVehicle() As Vehicle Implements
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
CreateVehicle.CreateAVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return New Boat
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Class
顯然,如果我們需要產生數十種交通工具則需要有數十個具體的工廠類。而這些工廠類的區別僅僅是返回相應的類的實例,所以為維護帶來了麻煩。如果需要在接口中增加一個帶參數的創建方法則所有的子類都不得需要修改。
在這個場合下,采用抽象工廠與工廠方法沒有區別。因為這裡並不涉及產品線,抽象工廠並不能解決其中有的問題。當然,如果每種交通工具都要有對應的車站,則要使用抽象工廠,但是將會更復雜。
2.采用反射技術簡化工廠類
有沒有可能將需要創建類的類型傳遞到工廠方法中,由工廠方法根據類型返回相應的實例?
解決這個問題的關鍵是需要動態決定需要創建的類,這不是設計模式能解決的問題屬於軟件平台的功能范疇。.NET可以提供相應的功能,即反射技術。
我們首先查看采用反射技術實現簡化的實例:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332237.gif)
Imports System.Reflection
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
Public Class CreateVehicleByType
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Implements CreateVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Private VeicleType As Type
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Sub New(ByVal t As Type)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
VeicleType = t
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Sub
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Function CreateAVehicle() As Vehicle Implements
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
CreateVehicle.CreateAVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim objConstructor As ConstructorInfo =
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
VeicleType.GetConstructor(System.Type.EmptyTypes)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim c As Vehicle = Ctype(objConstructou.Invoke(Nothing),Vehicle)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return c
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Class
在使用時,只要在創建時帶入需要創建的類的類型:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
Private Sub btcreateByType_Click(ByVal sender As System.Object,ByVal e As
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
System.EventArgs) Handles btCreateBytype.Clik
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
`根據選擇創建一個交通工具並執行GO
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim v As Vehicle `我們不知道需要創建的具體交通工具
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim f As CreateVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
If rCar.Checked Then
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
F = New CreateVehicleByType(GetType(car))
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
End If
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
If rTrain.Checked Then
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
F = New CreateVehicleByType(GetType(Train))
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
End If
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
If rBoat.Checked Then
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
F = New CreateVehicleByType(GetType(Boat))
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
End If
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
If rBus.Checked Then
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
F = New CreateVehicleByType(GetType(Boat))
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
End If
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
V = f.CreateAVehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
`執行GO指令
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
lbGO.Text = v.Go
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Sub
通過采用反射技術,我們將4個類簡化為一個類,並且在新增類型時不需要新的創建這樣,我們得到了簡化的工廠,可以將其稱為“反射工廠”。
3.對簡單工廠的改進
簡單工廠通過參數決定創建的類型,這些參數是在編程時預設的。因此在編譯後就無法修改,讓我們回顧代碼:
我們可以將這個工廠改造為反射工廠:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
Public Class clsCreateDB
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Shared Function CreateDB(ByVal strType As string, ByVal strConnString AsString) As _ clsAbstractDB
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Select Case strType.ToUpper
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Case “ORACLE”
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim myOracle As clsoracleDB
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
MyOracle = New clsOracleDB(strConnString)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return myOracle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Case “SQLSERVER”
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim mysqlserver As clsSQLServerDB
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Mysqlserver = New clsSQLServerDB(strConnString)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return mysqlserver
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Case Else
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim myoledb As clsOLEDB
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Myoledb = New clsOLEDB(strConnString)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return myoledb
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
End Select
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Class
這樣解決了簡單工廠必須依賴每個具體產品的問題,將表態依賴變為動態綁定.當引入新的數據庫類型時,不需要修改工廠即可滿足需要。
4.反射與工廠方法
如果工廠方法僅僅是為了獲得某個產品的實例,那麼完全可以使用反射技術來實現工廠方法。這樣解決了工廠方法的潛在問題,即當增加產品類時,必須增加相應的子類。
然而當工廠方法所存在的類不僅是實例化產品時,采用反射不一定是好辦法,因為可能使問題變得復雜。
5.反射與抽象工廠
可以采用反射來實現抽象工廠,這時抽象工廠可能變成了使用反射技術的具體工廠,不再有子類存在。創建交通系統的實例可以用如下的代碼來寫:
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332237.gif)
'<summary>
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332237.gif)
'VehicleSystemReflectionFactory 采用反射技術的工廠。
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332237.gif)
'</summary>
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332182.gif)
Public Class VehicleSystemReflectionFactory
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim vehicleType As String
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim vehicleStationType As String
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Sub New(ByVal vt As String, ByVal vst As String)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Me.vehicleType = vt
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Me.vehicleStationType = vst
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Sub
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Function GetVehicle() As Vehicle
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return CType(createbytype(Me.vehicleType), Vehicle)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Public Function GetVehicleStation() As VehicleStation
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return CType(createbytype(Me.vehicleStationType), VehicleStation)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332242.gif)
Private Function createbytype(ByVal vt As String) As Object
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim tt As Type
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
tt = Type.GetType(vt)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim ci As ConstructorInfo
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
ci = tt.GetConstructor(System.Type.EmptyTypes)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Dim null As System.DBNull
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332251.gif)
Return ci.Invoke(null)
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332287.gif)
End Function
![](https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017011316332299.gif)
End Class
這種情況下,抽象工廠就變為了只有一個類的反射工廠。
6.反射工廠的使用效果
使用反射工廠的優點是極大地減少了工廠類的數量、降低了代碼的冗余,並且系統更容易擴展,在增加新類型後,不需要修改工廠類。
使用反射工廠的代價是工廠與產品之間的依賴關系不明顯,由於是動態綁定,因此理論上可以用一個工廠完成很多類型的實例化,從而使得代碼 不容易理解。另外增大了測試難度,創建是動態完成的,測試用例的編寫和測試執行要比傳統的工廠困難。