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

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

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

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

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

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

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

  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层向用户提供反馈信息,而是由业务逻辑层担当了此任务,暂且就这么写吧,估计到后面写的多了就找到合适的方法了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结