就拿机房收费系统来说吧。
NUMBER ONE
单纯的用抽象工厂来实现。这样的好处,是从整个系统的全局出发,而不单单从原始的D看待,古人云:父母之爱子则为之计深远。这使得系统更容易扩展了。因为这里面除了SQLHelper都使用了实体包,实体包的线就省略了。
NUMBER TWO
用"简单工厂"去改造的抽象工厂。
这里说的简单工厂只是因为它没有工厂接口。而事实上因为我们的机房收费系统是用对一簇产品进行创建使用,按理说一簇产品应该是抽象工厂的。这样的好处是,在D层实现了解耦,和抽象工厂比起来,我们要扩展的话,BLL层和IFactory改动较大。
NUMBER THREE
用"简单工厂"和抽象工厂结合改造后,再加上配置文件。
与NUMBER ONE不同的是去掉一条线。这样子的好处是去除了DALFactory与实际功能类DAL的耦合。
当然如果找个平衡点的话,我们的最佳选择是用NUMBER THREE。
代码如下。(一下代码均以登录为例。UI省略)
NUMBER ONE
BLL层。
'------------------------------------------------------------------------------ ' <copyright file="DALUser.vb" company="FANG"> ' Copyright (c) 2012 FANG. All rights reserved. ' <copyright> ' <author>The Sky Always Sunshine<author> ' <author>我的博客地址http://blog.csdn.net/xhf55555</author> ' <date>2012年2月3日<date> ' <description> 'BLL层之登录。 ' <description> '------------------------------------------------------------------------------ Public Class BLL_Login Public Function Login(ByVal User As Entity.EN_User) As Boolean Dim myUser As New Entity.EN_User '确定实例化哪个数据库给Factory,来实现换DB。 Dim factory As Ifactory.Ifactory = New DALFactory.SqlserverFactory Dim IUser As IDAL.IUser '与具体的数据库访问解除了依赖。 IUser = factory.CheckUser() myUser = IUser.GetUser(User) If myUser.UserPwd = User.UserPwd Then Return True Else Return False End If End Function End Class
IDAL层。
''' <summary> ''' 用户表功能接口。 ''' </summary> ''' <remarks></remarks> Public Interface IUser Function GetUser(ByVal User As Entity.EN_User) As Entity.EN_User End Interface
DAL层。(以SqlserverUser为例。)
Public Class SqlserverUser : Implements IDAL.IUser Public Function GetUser(ByVal User As Entity.EN_User) As Entity.EN_User Implements IDAL.IUser.GetUser Dim ConnStr As String = "Data Source=192.168.24.169;Initial Catalog=PC_ChargeSys;User ID=sa;Pwd=123456" Dim conn As SqlConnection = New SqlConnection(ConnStr) ' Dim connection As New SQLHelp.ConnectionHelp Dim sql As String = "select * from tb_User where UserID='" & User.UserID & "'" Dim cmd As SqlCommand = New SqlCommand(sql, conn) Dim read As SqlDataReader Dim myUser As New Entity.EN_User Try conn.Open() read = cmd.ExecuteReader read.Read() myUser.UserID = read.Item("") myUser.UserPwd = read.Item("UserPwd") Return myUser Catch ex As Exception myUser.UserID = 0 myUser.UserPwd = "" Return myUser End Try End Function End Class
IFactory层。
''' <summary> ''' 定义操作工厂接口。 ''' </summary> ''' <remarks></remarks> Public Interface Ifactory Function CheckUser() As IDAL.IUser End Interface
SqlserverFactory具体操作工程类。
'------------------------------------------------------------------------------ ' <copyright file="DALUser.vb" company="FANG"> ' Copyright (c) 2012 FANG. All rights reserved. ' <copyright> ' <author>The Sky Always Sunshine<author> ' <author>我的博客地址http://blog.csdn.net/xhf55555</author> ' <date>2012年2月3日<date> ' <description> '具体的操作类工厂实现抽象的功能类接口。 ' <description> '------------------------------------------------------------------------------ Public Class SqlserverFactory : Implements Ifactory.Ifactory Public Function CheckUser() As IDAL.IUser Implements Ifactory.Ifactory.CheckUser Return New DAL.SqlserverUser End Function End Class
AccessFactory(与Sqlserver相似,不再赘余。)
User实体类。
'User实体类。 Public Class EN_User Dim intUserID As Integer '定义用户编号变量。 Dim strUserName As String '定义用户姓名变量。 Dim strUserPwd As String '定义用户密码变量名。 Dim strUserActor As String '定义用户角色变量。 Dim vntUserRegDate As Date '定义用户注册日期。 Dim strUserFlag As String '定义用户是否合法的标记的变量。 Dim strUserType As String '定义用户类型变量。 ''' <summary> ''' 用户编号属性方法。 ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserID() As Integer Get Return intUserID End Get Set(ByVal value As Integer) intUserID = value End Set End Property ''' <summary> ''' 定义用户名属性方法。 ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserName() As String Get Return strUserName End Get Set(ByVal value As String) strUserName = value End Set End Property ''' <summary> ''' 定义用户密码属性方法 。 ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserPwd() As String Get Return strUserPwd End Get Set(ByVal value As String) strUserPwd = value End Set End Property ''' <summary> ''' 定义用户角色属性方法。 ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserActor() As String Get Return strUserActor End Get Set(ByVal value As String) strUserActor = value End Set End Property ''' <summary> ''' 定义注册日期变量。 ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserRegDate As Date Get Return vntUserRegDate End Get Set(ByVal value As Date) vntUserRegDate = value End Set End Property ''' <summary> ''' 定义用户是否合法的标记。(看是否是已经注销) ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserFlag As String Get Return strUserFlag End Get Set(ByVal value As String) strUserFlag = value End Set End Property ''' <summary> ''' 定义用户类型变量(是固定用户还是临时用户) ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property UserTyep() As String Get Return strUserType End Get Set(ByVal value As String) strUserType = value End Set End Property End Class
我们再看NUMBER TWO 用简单工厂改造的抽象工厂。
我们是去掉了IFactory工厂接口和他手下的具体工厂操作类,而用一个DALFactory代替解决。这样把对功能类的判断放到了DALFactory里通过SelectCase来进行判断而不是通过实例化来判断了。
UI、IUser、AccessUser、SqlserverUser是不变的。所以在以上基础上改变 ,代码如下。
BLL层代码。
''' <summary> ''' 用户登录业务逻辑。 ''' </summary> ''' <remarks></remarks> Public Class BLL_Login Public Function Login(ByVal User As Entity.EN_User) As Boolean Dim myUser As New Entity.EN_User '通过具体的操作工厂实现要判断使用哪个数据库。 Dim Dalfactory As New DALFactory.DFactory Dim IUser As IDAL.IUser '与具体的数据库访问解除了依赖。 IUser = Dalfactory.CreateUserInfo() myUser = IUser.GetUser(User) If myUser.UserPwd = User.UserPwd Then Return True Else Return False End If End Function End Class
DALFactory层。
Imports IDAL ''' <summary> ''' 操作工厂类。 ''' </summary> ''' <remarks></remarks> Public Class DFactory 'Dim DataBase As String = "Access" Dim DataBase As String = "Sql" Function CreateUserInfo() As IDAL.IUser Dim db As IUser Select Case DataBase Case "Sql" db = New DAL.SqlserverUser 'Case "Access" ' db = New DAL.AccessUser End Select Return db End Function
NUMBER THREE我们是改变了操作工厂case而用反射的方法,和case说拜拜。我们用case判断太过于发死,把字符串写死在了DALFactory中,我们对功能类的使用,不是功能类本身去决定自己。我们要自己决定自己的人生大事,所以用反射就可以了。这样解除了分支判断的耦合。
DALFactory代码。
Imports IDAL Imports System.Reflection ''' <summary> ''' 操作工厂类。 ''' </summary> ''' <remarks></remarks> Public Class DFactory 'Dim DataBase As String = "Access" 'Dim DataBase As String = "Sql" Dim strDB As String = System.Configuration.ConfigurationSettings.AppSettings("DBString") Function CreateUserInfo() As IDAL.IUser Return CType(Assembly.Load("DAL").CreateInstance("DAL.SqlserverUser" & strDB), IDAL.IUser) End Function End Class
具体的功能类SqlserverUser,只是改了一句话。
Public Class SqlserverUser : Implements IDAL.IUser
Public Function GetUser(ByVal User As Entity.EN_User) As Entity.EN_User Implements IDAL.IUser.GetUser
'Dim ConnStr As String = "Data Source=192.168.24.169;Initial Catalog=PC_ChargeSys;User ID=sa;Pwd=123456"
Dim ConnStr As String = System.Configuration.ConfigurationSettings.AppSettings("ConnStr")
Dim conn As SqlConnection = New SqlConnection(ConnStr)
'Dim connection As New SQLHelp.ConnectionHelp
'connection.Connect()
Dim sql As String = "select * from tb_User where UserID='" & User.UserID & "'"
Dim cmd As SqlCommand = New SqlCommand(sql, conn)
Dim read As SqlDataReader
Dim myUser As New Entity.EN_User
Try
conn.Open()
read = cmd.ExecuteReader
read.Read()
myUser.UserID = read.Item("")
myUser.UserPwd = read.Item("UserPwd")
Return myUser
Catch ex As Exception
myUser.UserID = 0
myUser.UserPwd = ""
Return myUser
End Try
End Function
End Class
工厂模式的基本原理见:http://blog.csdn.net/xhf55555/article/details/7609272
综上,我们把简单工厂、工厂方法、和抽象工厂三个模式在实际应用中进行了比较。我们的最佳组合是简单工厂改造的抽象工厂加上配置文件,耦合度和系统的开闭(对扩展开放、对修改封闭)、系统的可维护和灵活性尽在我们的三个包图中。笔者(me)认为,我们的设计模式就像数学公式,灵活运用就好。我们在开发一个系统的时候思考问题不要从上向下的思考方式,我们要从下向上,不是因为解耦而解耦,而是我们从系统长远的角度出发,使得我们在系统在不断的重构中发现问题,才去不断的思考,进一步的抽象,使得系统更加完美。没有完美的系统,只有完美的过程。
问题多多,欢迎您前来指教!