加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

抽象工厂+反射+依赖注入 实现对数据访问层和业务逻辑层的优化

发布时间:2020-12-13 22:12:24 所属栏目:百科 来源:网络整理
导读:分层思想的一个核心就是部件化,各个层之间是相互独立的,每一层可以随便抽取换成一个其他语言的版本,但只要与相应的接口吻合就行。 我用的三层架构大致是这样的,基本的三层就不说了,然后分别为业务逻辑层和数据访问层定义一个接口,由具体的那个层来实现

分层思想的一个核心就是部件化,各个层之间是相互独立的,每一层可以随便抽取换成一个其他语言的版本,但只要与相应的接口吻合就行。

我用的三层架构大致是这样的,基本的三层就不说了,然后分别为业务逻辑层和数据访问层定义一个接口,由具体的那个层来实现,问题产生了,由谁来指定程序使用哪个具体的对象来实现相应接口?

为解决这个问题,我应用的是抽象工厂模式。分别为业务逻辑层和数据访问层添加一个抽象工厂。具体架构还是看下图吧。

这里的Utility是一个工具类,在下文中会提到。

学过设计模式的人都应该听过反射技术,但是一个系统中用到的类很多,需要对每一个类进行实例化,如果仅利用抽象工厂+反射模式,重复的代码比较多,如果哪一天整个DAL层发生变更,那么就要在代码中修改每一个用到的地方,不仅不容易维护,而且还很容易出错,未解决这个问题,对程序作了一个优化——用到依赖注入。还是看看代码吧。

1、先看看依赖注入的容器:这里我把这个注入容器放到了工具类中,刚开始学习设计模式,不知道是否合理,欢迎高手们指点。

[vb] view plain copy
  1. ImportsSystem.Configuration
  2. ImportsSystem.Reflection
  3. PublicClassDependencyInjector
  4. '''<summary>
  5. '''利用反射机制,取得数据访问层对象
  6. '''</summary>
  7. '''<paramname="className">传入数据访问层中要实例化的类的名称</param>
  8. '''<returns>指定的数据访问层的类</returns>
  9. '''<remarks></remarks>
  10. FunctionGetDALObject(ByValclassNameAsString)Object
  11. DimdalObject
  12. DimdalNameString
  13. DimfullClassNameString
  14. DimdalObj'通过配置文件的指定要应用的DAL层
  15. dal=System.Configuration.ConfigurationManager.AppSettings("DAL")
  16. 'dalName就是常说的用用程序的名称
  17. dalName=dal.ToString
  18. '命名空间+类的名称,指明要实例化的类的路径
  19. fullClassName=dalName+"."+className
  20. '通过反射,取得数据访问层对象
  21. dalObj=Assembly.Load(dalName).CreateInstance(fullClassName)
  22. '返回指定的对象
  23. ReturndalObj
  24. EndFunction
  25. '''取得指定业务逻辑层的指定类
  26. '''<paramname="className">要应用的业务逻辑层的具体类的名称</param>
  27. '''<returns>指定的业务逻辑层的类(对象)</returns>
  28. FunctionGetBLLObject(DimbllDimbllNameDimbllObj'从配置文件中读取业务逻辑名称
  29. bll=System.Configuration.ConfigurationManager.AppSettings("BLL")
  30. bllName=bll.ToString
  31. fullClassName=bllName+"."+className
  32. '利用反射取得业务逻辑层对象
  33. bllObj=Assembly.Load(bllName).CreateInstance(fullClassName)
  34. ReturnbllObj
  35. Function
  36. Class

2、相关配置文件:

[html] copy
    <appSettings>
  1. addkey="connStr"value="PersistSecurityInfo=true;DataSource=*****;InitialCatalog=Charge_Sys_SelfDesign;UserID=sa;PWD=****;"/>
  2. addkey="DAL"value="DAL"/>
  3. addkey="BLL"value="BLL"</>

3、业务逻辑层工厂:这里以在工厂中生产一个UserBLL类为例,在工厂中添加CreateUserBLL()方法,理论上讲,业务逻辑层有多少个类在此工厂中就要有多少个相应的方法,但是针对不同语言写的或者是不同的程序员用同一种语言写的同一层的代码(但都实现了程序指定的接口),我们在给类起名字的时候,只要用相同的类名就可以通过仅修改配置文件,达到换层的目的,而无需在工厂中改动任何代码。比如说,我现在要把DAL层换成AccessDAL,那么仅需要做如下修改即可。

copy
    addkey="DAL"value="AccessDAL"/>

这就是分层的好处,当然也是面向对象思想的优势了。

上面主要是解释了一下配置文件,来看看业务逻辑工厂代码:

copy
    ImportsIBLL
  1. ImportsUtility
  2. ImportsSystem.Windows.Forms
  3. ClassCreateBLL
  4. PrivatedependecyNewUtility.DependencyInjector
  5. '''<summary>
  6. '''生成用户业务逻辑层访问对象
  7. '''</summary>
  8. '''<returns></returns>
  9. FunctionCreateUserBLL()AsIBLL.IUserBLL
  10. Try
  11. '如果不用注入,需要重复N次BLL,
  12. 'ReturnCType(Assembly.Load("BLL").CreateInstance("BLL.UserBLL"),IBLL.IUserBLL)
  13. '优化后只需指明具体类名,如果要换层,只需到配置文件中做简单修改即可,程序代码清晰,不宜出错。
  14. ReturnCType(dependecy.GetBLLObject("UserBLL"),IBLL.IUserBLL)
  15. CatchexAsException
  16. MsgBox(ex.Message)
  17. Nothing
  18. Class

4、数据访问层工厂代码类似:

copy
    ImportsUtility
  1. ImportsSystem.Configuration
  2. ImportsSystem.Reflection
  3. ImportsIDAL
  4. ClassCreateDAL
  5. PrivatedependencyNewUtility.DependencyInjector
  6. '''生成用户指定的数据访问层对象
  7. '''<returns></returns>
  8. '''<remarks></remarks>
  9. FunctionCreateUserDAL()AsIDAL.IUserDAL
  10. Try
  11. 'ReturnCType(Assembly.Load("DAL").CreateInstance("DAL.UserDAL"),IDAL.IUserDAL)
  12. CType(dependency.GetDALObject("UserDAL"),IDAL.IUserDAL)
  13. AsException
  14. MessageBox.Show(ex.Message)
  15. Nothing
  16. Class

5、业务逻辑层接口代码比较简单,只需要定义一些相关接口方法即可,但是这里面体现了系统的架构,看似代码简单,实则反应程序员的架构水平。

copy
    InterfaceIUserBLL
  1. '返回用户登录的身份级别,在接口的实现中要验证用户名和密码
  2. FunctionLogIn(ByValmodelUserAsModel.User)Interface

6、数据访问层接口:

copy
    InterfaceIUserDAL
  1. '获取用户ID
  2. FunctionGetID('获取用户密码
  3. FunctionGetPwd('获取用户级别
  4. FunctionGetLevel(Interface

7、业务逻辑层:

copy
    ImportsDALFactory
  1. ImportsIBLL
  2. ImportsIDAL
  3. ImportsModel
  4. ImportsSystem.Data.SqlClient
  5. ImportsSystem.Collections.Generic
  6. ClassUserBLL
  7. ImplementsIBLL.IUserBLL
  8. PrivatedalFactoryNewDALFactory.CreateDAL
  9. '''先判断用户名和密码是否正确,然后返回用户级别
  10. '''<paramname="modelUser">用户实体类</param>
  11. '''<returns>用户级别</returns>
  12. StringImplementsIBLL.IUserBLL.LogIn
  13. DimuserLevelIfdalFactory.CreateUserDAL.GetID(modelUser)=""Then
  14. MsgBox("用户名错误!")
  15. ExitIf
  16. IfdalFactory.CreateUserDAL.GetPwd(modelUser)=""Then
  17. MsgBox("密码名错误!")
  18. If
  19. '通过数据访问层工厂指定实现数据访问层接口的具体的数据访问层以及该层的具体类
  20. userLevel=dalFactory.CreateUserDAL.GetLevel(modelUser)
  21. ReturnuserLevel
  22. Class

8、数据访问层:

copy
    ClassUserDAL
  1. '实现数据访问层的接口
  2. ImplementsIDAL.IUserDAL
  3. PrivatesqlHelpNewUtility.SQLServerDALHelp
  4. '''读取用户ID
  5. '''<paramname="modelUser">用户实体类</param>
  6. '''<returns>用户ID</returns>
  7. ImplementsIDAL.IUserDAL.GetID
  8. DimUser_IDDimconnNewSqlConnection(sqlHelp.connStr)
  9. DimspName spName="proc_GetUserID"
  10. DimcmdNewSqlCommand(spName,conn)
  11. cmd.CommandType=CommandType.StoredProcedure
  12. DimParamAsSqlParameter
  13. Param=NewSqlParameter("@User_ID",SqlDbType.VarChar)
  14. Param.Value=modelUser.User_ID
  15. cmd.Parameters.Add(Param)
  16. conn.Open()
  17. User_ID=cmd.ExecuteScalar.ToString
  18. ReturnUser_ID
  19. ThrowNewException(ex.Message)
  20. Finally
  21. conn.Close()
  22. cmd.Dispose()
  23. cmd=ReturnUser_ID
  24. '''读取用户密码
  25. '''<returns>密码</returns>
  26. ImplementsIDAL.IUserDAL.GetPwd
  27. Dimuser_Pwd spName="proc_GetUserPwd"
  28. AsSqlParameter
  29. Param=NewSqlParameter("@User_Pwd",SqlDbType.VarChar)
  30. Param.Value=modelUser.User_Pwd
  31. cmd.Parameters.Add(Param)
  32. user_Pwd=cmd.ExecuteScalar.ToString
  33. MsgBox(ex.Message)
  34. NewException(ex.Message)
  35. Finally
  36. conn.Close()
  37. cmd.Dispose()
  38. cmd=Returnuser_Pwd
  39. '''读取用户身份级别
  40. '''<returns>身份级别</returns>
  41. ImplementsIDAL.IUserDAL.GetLevel
  42. DimUser_Level spName="proc_GetUserLevel"
  43. NewSqlConnection(sqlHelp.connStr)
  44. cmd.CommandType=CommandType.StoredProcedure
  45. conn.Open()
  46. User_Level=cmd.ExecuteScalar.ToString
  47. ReturnUser_Level
  48. Class

9、总算到UI层了:注意这里还没有添加针对用户输入合法性的验证,如,用户名、密码输入是否为空,是否符合指定格式等。

copy
    ImportsModel
  1. ImportsSystem.Collections.Generic
  2. ImportsBLLFactory
  3. ClassfrmLogIn
  4. PrivatebllFactoryNewBLLFactory.CreateBLL
  5. PrivateSubbtnLogIn_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnLogIn.Click
  6. DimmodelUserNewModel.User
  7. modelUser.User_ID=txtUserID.Text
  8. modelUser.User_Pwd=txtUserPwd.Text
  9. '通过业务逻辑工厂指定业务逻辑接口要使用的具体的业务逻辑层中的具体类
  10. userLevel=bllFactory.CreateUserBLL.LogIn(modelUser)
  11. SelectCaseuserLevel
  12. Case"一般用户"
  13. Case"操作员"
  14. Case"管理员"
  15. frmMDIParentForm.Show()
  16. CaseElse
  17. MsgBox("未知错误!")
  18. Sub
  19. Select
  20. SubbtnExit_Click(HandlesbtnExit.Click
  21. txtUserID.Text=""
  22. txtUserPwd.Text=""
  23. Me.Close()
  24. Class

到此为止,一个简单的三层架构就算实现了,隐约感觉设计上有些地方不合理,但说不好存在于哪些地方,希望由此路过的大牛们,多多指教,另外,这里的数据访问层代码冗余较多,在后续的博客中,会通过编写一个SQLHelp来实现优化,还有UML包图也会在后续博客中天上。

最后说说我自己的感触。

针对架构:用到了两个抽象工厂,刚开始是将两个工厂写到了一层中,这层的名称就叫Factory,而且封装注入的类也一并放到了这层中,但是在调试程序的时候出现DAL层依赖循环调用错误,不知道是代码还是设计上的原因?

针对接口设计:不太清楚业务逻辑层的方法设计是否合理,现在的一个问题就是如果用户名或者密码出错的话,并不是由UI层向用户提供反馈信息,而是由业务逻辑层担当了此任务,暂且就这么写吧,估计到后面写的多了就找到合适的方法了。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读