工厂模式的应用实例
就拿机房收费系统来说吧。
NUMBER ONE
单纯的用抽象工厂来实现。这样的好处,是从整个系统的全局出发,而不单单从原始的D看待,古人云:父母之爱子则为之计深远。这使得系统更容易扩展了。因为这里面除了SQLHelper都使用了实体包,实体包的线就省略了。
NUMBER TWO
用"简单工厂"去改造的抽象工厂。
这里说的简单工厂只是因为它没有工厂接口。而事实上因为我们的机房收费系统是用对一簇产品进行创建使用,按理说一簇产品应该是抽象工厂的。这样的好处是,在D层实现了解耦,和抽象工厂比起来,我们要扩展的话,BLL层和IFactory改动较大。
NUMBER THREE
用"简单工厂"和抽象工厂结合改造后,再加上配置文件。
与NUMBER ONE不同的是去掉一条线。这样子的好处是去除了DALFactory与实际功能类DAL的耦合。
当然如果找个平衡点的话,我们的最佳选择是用NUMBER THREE。
代码如下。(一下代码均以登录为例。UI省略)
NUMBER ONE
BLL层。
'------------------------------------------------------------------------------ '<copyrightfile="DALUser.vb"company="FANG"> 'Copyright(c)2012FANG.Allrightsreserved. '<copyright> '<author>TheSkyAlwaysSunshine<author> '<author>我的博客地址http://blog.****.net/xhf55555</author> '<date>2012年2月3日<date> '<description> 'BLL层之登录。 '<description> '------------------------------------------------------------------------------ PublicClassBLL_Login PublicFunctionLogin(ByValUserAsEntity.EN_User)AsBoolean DimmyUserAsNewEntity.EN_User '确定实例化哪个数据库给Factory,来实现换DB。 DimfactoryAsIfactory.Ifactory=NewDALFactory.SqlserverFactory DimIUserAsIDAL.IUser '与具体的数据库访问解除了依赖。 IUser=factory.CheckUser() myUser=IUser.GetUser(User) IfmyUser.UserPwd=User.UserPwdThen ReturnTrue Else ReturnFalse EndIf EndFunction EndClass
IDAL层。
DAL层。(以SqlserverUser为例。)
PublicClassSqlserverUser:ImplementsIDAL.IUser PublicFunctionGetUser(ByValUserAsEntity.EN_User)AsEntity.EN_UserImplementsIDAL.IUser.GetUser DimConnStrAsString="DataSource=192.168.24.169;InitialCatalog=PC_ChargeSys;UserID=sa;Pwd=123456" DimconnAsSqlConnection=NewSqlConnection(ConnStr) 'DimconnectionAsNewSQLHelp.ConnectionHelp DimsqlAsString="select*fromtb_UserwhereUserID='"&User.UserID&"'" DimcmdAsSqlCommand=NewSqlCommand(sql,conn) DimreadAsSqlDataReader DimmyUserAsNewEntity.EN_User Try conn.Open() read=cmd.ExecuteReader read.Read() myUser.UserID=read.Item("") myUser.UserPwd=read.Item("UserPwd") ReturnmyUser CatchexAsException myUser.UserID=0 myUser.UserPwd="" ReturnmyUser EndTry EndFunction EndClass
IFactory层。
'''<summary> '''定义操作工厂接口。 '''</summary> '''<remarks></remarks> PublicInterfaceIfactory FunctionCheckUser()AsIDAL.IUser EndInterface
SqlserverFactory具体操作工程类。
AccessFactory(与Sqlserver相似,不再赘余。)
User实体类。
'User实体类。 PublicClassEN_User DimintUserIDAsInteger'定义用户编号变量。 DimstrUserNameAsString'定义用户姓名变量。 DimstrUserPwdAsString'定义用户密码变量名。 DimstrUserActorAsString'定义用户角色变量。 DimvntUserRegDateAsDate'定义用户注册日期。 DimstrUserFlagAsString'定义用户是否合法的标记的变量。 DimstrUserTypeAsString'定义用户类型变量。 '''<summary> '''用户编号属性方法。 '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserID()AsInteger Get ReturnintUserID EndGet Set(ByValvalueAsInteger) intUserID=value EndSet EndProperty '''<summary> '''定义用户名属性方法。 '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserName()AsString Get ReturnstrUserName EndGet Set(ByValvalueAsString) strUserName=value EndSet EndProperty '''<summary> '''定义用户密码属性方法。 '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserPwd()AsString Get ReturnstrUserPwd EndGet Set(ByValvalueAsString) strUserPwd=value EndSet EndProperty '''<summary> '''定义用户角色属性方法。 '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserActor()AsString Get ReturnstrUserActor EndGet Set(ByValvalueAsString) strUserActor=value EndSet EndProperty '''<summary> '''定义注册日期变量。 '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserRegDateAsDate Get ReturnvntUserRegDate EndGet Set(ByValvalueAsDate) vntUserRegDate=value EndSet EndProperty '''<summary> '''定义用户是否合法的标记。(看是否是已经注销) '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserFlagAsString Get ReturnstrUserFlag EndGet Set(ByValvalueAsString) strUserFlag=value EndSet EndProperty '''<summary> '''定义用户类型变量(是固定用户还是临时用户) '''</summary> '''<value></value> '''<returns></returns> '''<remarks></remarks> PublicPropertyUserTyep()AsString Get ReturnstrUserType EndGet Set(ByValvalueAsString) strUserType=value EndSet EndProperty EndClass我们再看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层。
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,只是改了一句话。
工厂模式的基本原理见:http://blog.****.net/xhf55555/article/details/7609272
综上,我们把简单工厂、工厂方法、和抽象工厂三个模式在实际应用中进行了比较。我们的最佳组合是简单工厂改造的抽象工厂加上配置文件,耦合度和系统的开闭(对扩展开放、对修改封闭)、系统的可维护和灵活性尽在我们的三个包图中。笔者(me)认为,我们的设计模式就像数学公式,灵活运用就好。我们在开发一个系统的时候思考问题不要从上向下的思考方式,我们要从下向上,不是因为解耦而解耦,而是我们从系统长远的角度出发,使得我们在系统在不断的重构中发现问题,才去不断的思考,进一步的抽象,使得系统更加完美。没有完美的系统,只有完美的过程。
问题多多,欢迎您前来指教!