关闭

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

515人阅读 评论(0) 收藏 举报
分类:

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

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

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

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

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

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

  1. Imports System.Configuration  
  2. Imports System.Reflection  
  3. Public Class DependencyInjector  
  4.   
  5.     ''' <summary>  
  6.     ''' 利用反射机制,取得数据访问层对象  
  7.     ''' </summary>  
  8.     ''' <param name="className">传入数据访问层中要实例化的类的名称</param>  
  9.     ''' <returns>指定的数据访问层的类</returns>  
  10.     ''' <remarks></remarks>  
  11.     Public Function GetDALObject(ByVal className As StringAs Object  
  12.         Dim dal As Object  
  13.         Dim dalName As String  
  14.         Dim fullClassName As String  
  15.         Dim dalObj As Object  
  16.   
  17.         '通过配置文件的指定要应用的DAL层  
  18.         dal = System.Configuration.ConfigurationManager.AppSettings("DAL")  
  19.   
  20.         'dalName就是常说的用用程序的名称  
  21.         dalName = dal.ToString  
  22.   
  23.         '命名空间+类的名称,指明要实例化的类的路径  
  24.         fullClassName = dalName + "." + className  
  25.         '通过反射,取得数据访问层对象  
  26.         dalObj = Assembly.Load(dalName).CreateInstance(fullClassName)  
  27.         '返回指定的对象  
  28.         Return dalObj  
  29.   
  30.     End Function  
  31.   
  32.   
  33.     ''' <summary>  
  34.     '''取得指定业务逻辑层的指定类  
  35.     ''' </summary>  
  36.     ''' <param name="className">要应用的业务逻辑层的具体类的名称</param>  
  37.     ''' <returns>指定的业务逻辑层的类(对象)</returns>  
  38.     ''' <remarks></remarks>  
  39.     Public Function GetBLLObject(ByVal className As StringAs Object  
  40.   
  41.         Dim bll As Object  
  42.         Dim bllName As String  
  43.         Dim bllObj As Object  
  44.         Dim fullClassName As String  
  45.   
  46.         '从配置文件中读取业务逻辑名称  
  47.         bll = System.Configuration.ConfigurationManager.AppSettings("BLL")  
  48.         bllName = bll.ToString  
  49.   
  50.         fullClassName = bllName + "." + className  
  51.   
  52.         '利用反射取得业务逻辑层对象  
  53.         bllObj = Assembly.Load(bllName).CreateInstance(fullClassName)  
  54.   
  55.         Return bllObj  
  56.   
  57.     End Function  
  58. End Class  

2、相关配置文件:

[html] view plaincopy
  1. <appSettings>  
  2.     <add key="connStr"  value="Persist Security Info=true;Data Source=*****;Initial Catalog=Charge_Sys_SelfDesign;User ID=sa;PWD=****;" />  
  3.     <add key="DAL"  value="DAL" />  
  4.     <add key="BLL"  value="BLL" />      
  5.   </appSettings>  

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

[html] view plaincopy
  1. <add key="DAL"  value="AccessDAL" />  

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

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

  1. Imports IBLL  
  2. Imports Utility  
  3. Imports System.Configuration  
  4. Imports System.Reflection  
  5. Imports System.Windows.Forms  
  6. Public Class CreateBLL  
  7.   
  8.     Private dependecy As New Utility.DependencyInjector  
  9.   
  10.     ''' <summary>  
  11.     ''' 生成用户业务逻辑层访问对象  
  12.     ''' </summary>  
  13.     ''' <returns></returns>  
  14.     ''' <remarks></remarks>  
  15.     Public Function CreateUserBLL() As IBLL.IUserBLL  
  16.         Try  
  17.             '如果不用注入,需要重复N次BLL,  
  18.             'Return CType(Assembly.Load("BLL").CreateInstance("BLL.UserBLL"), IBLL.IUserBLL)  
  19.             '优化后只需指明具体类名,如果要换层,只需到配置文件中做简单修改即可,程序代码清晰,不宜出错。  
  20.             Return CType(dependecy.GetBLLObject("UserBLL"), IBLL.IUserBLL)  
  21.         Catch ex As Exception  
  22.             MsgBox(ex.Message)  
  23.             Return Nothing  
  24.         End Try  
  25.     End Function  
  26. End Class  

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

  1. Imports Utility  
  2. Imports System.Configuration  
  3. Imports System.Reflection  
  4. Imports IDAL  
  5. Imports System.Windows.Forms  
  6.   
  7. Public Class CreateDAL  
  8.   
  9.     Private dependency As New Utility.DependencyInjector  
  10.   
  11.     ''' <summary>  
  12.     ''' 生成用户指定的数据访问层对象  
  13.     ''' </summary>  
  14.     ''' <returns></returns>  
  15.     ''' <remarks></remarks>  
  16.     Public Function CreateUserDAL() As IDAL.IUserDAL  
  17.         Try  
  18.             'Return CType(Assembly.Load("DAL").CreateInstance("DAL.UserDAL"), IDAL.IUserDAL)  
  19.             Return CType(dependency.GetDALObject("UserDAL"), IDAL.IUserDAL)  
  20.         Catch ex As Exception  
  21.             MessageBox.Show(ex.Message)  
  22.             Return Nothing  
  23.         End Try  
  24.   
  25.     End Function  
  26. End Class  

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

  1. Public Interface IUserBLL  
  2.   
  3.     '返回用户登录的身份级别,在接口的实现中要验证用户名和密码  
  4.     Function LogIn(ByVal modelUser As Model.User) As String  
  5.   
  6. End Interface  

6、数据访问层接口:

  1. Public Interface IUserDAL  
  2.   
  3.     '获取用户ID  
  4.     Function GetID(ByVal modelUser As Model.User) As String  
  5.     '获取用户密码  
  6.     Function GetPwd(ByVal modelUser As Model.User) As String  
  7.     '获取用户级别  
  8.     Function GetLevel(ByVal modelUser As Model.User) As String  
  9.   
  10. End Interface  

7、业务逻辑层:

  1. Imports DALFactory  
  2. Imports IBLL  
  3. Imports IDAL  
  4. Imports Model  
  5. Imports System.Data.SqlClient  
  6. Imports System.Collections.Generic  
  7. Public Class UserBLL  
  8.     Implements IBLL.IUserBLL  
  9.     Private dalFactory As New DALFactory.CreateDAL  
  10.     ''' <summary>  
  11.     ''' 先判断用户名和密码是否正确,然后返回用户级别  
  12.     ''' </summary>  
  13.     ''' <param name="modelUser">用户实体类</param>  
  14.     ''' <returns>用户级别</returns>  
  15.     ''' <remarks></remarks>  
  16.     Public Function LogIn(ByVal modelUser As Model.User) As String Implements IBLL.IUserBLL.LogIn  
  17.         Dim userLevel As String  
  18.         Try  
  19.             If dalFactory.CreateUserDAL.GetID(modelUser) = "" Then  
  20.                 MsgBox("用户名错误!")  
  21.                 Return Nothing  
  22.                 Exit Function  
  23.             End If  
  24.             If dalFactory.CreateUserDAL.GetPwd(modelUser) = "" Then  
  25.                 MsgBox("密码名错误!")  
  26.                 Return Nothing  
  27.                 Exit Function  
  28.             End If  
  29.   
  30.   
  31.             '通过数据访问层工厂指定实现数据访问层接口的具体的数据访问层以及该层的具体类  
  32.             userLevel = dalFactory.CreateUserDAL.GetLevel(modelUser)  
  33.   
  34.   
  35.         Catch ex As Exception  
  36.             Return Nothing  
  37.         End Try  
  38.         Return userLevel  
  39.     End Function  
  40. End Class  

 

8、数据访问层:

  1. Imports System.Data.SqlClient  
  2. Imports Utility  
  3. Imports System.Windows.Forms  
  4.   
  5. Public Class UserDAL  
  6.     '实现数据访问层的接口  
  7.     Implements IDAL.IUserDAL  
  8.     Private sqlHelp As New Utility.SQLServerDALHelp  
  9.     ''' <summary>  
  10.     ''' 读取用户ID  
  11.     ''' </summary>  
  12.     ''' <param name="modelUser">用户实体类</param>  
  13.     ''' <returns>用户ID</returns>  
  14.     ''' <remarks></remarks>  
  15.     Public Function GetID(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetID  
  16.         Dim User_ID As String  
  17.         Dim conn As New SqlConnection(sqlHelp.connStr)  
  18.         Dim spName As String  
  19.         spName = "proc_GetUserID"  
  20.         Dim cmd As New SqlCommand(spName, conn)  
  21.         cmd.CommandType = CommandType.StoredProcedure  
  22.         Dim Param As SqlParameter  
  23.         Param = New SqlParameter("@User_ID", SqlDbType.VarChar)  
  24.         Param.Value = modelUser.User_ID  
  25.         cmd.Parameters.Add(Param)  
  26.   
  27.         Try  
  28.             conn.Open()  
  29.             User_ID = cmd.ExecuteScalar.ToString  
  30.             Return User_ID  
  31.         Catch ex As Exception  
  32.             MsgBox(ex.Message)  
  33.             Throw New Exception(ex.Message)  
  34.         Finally  
  35.             conn.Close()  
  36.             cmd.Dispose()  
  37.             cmd = Nothing  
  38.         End Try  
  39.         Return User_ID  
  40.     End Function  
  41.     ''' <summary>  
  42.     ''' 读取用户密码  
  43.     ''' </summary>  
  44.     ''' <param name="modelUser">用户实体类</param>  
  45.     ''' <returns>密码</returns>  
  46.     ''' <remarks></remarks>  
  47.     Public Function GetPwd(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetPwd  
  48.         Dim user_Pwd As String  
  49.         Dim spName As String  
  50.         spName = "proc_GetUserPwd"  
  51.         Dim conn As New SqlConnection(sqlHelp.connStr)  
  52.         Dim cmd As New SqlCommand(spName, conn)  
  53.         cmd.CommandType = CommandType.StoredProcedure  
  54.   
  55.         Dim Param As SqlParameter  
  56.         Param = New SqlParameter("@User_Pwd", SqlDbType.VarChar)  
  57.         Param.Value = modelUser.User_Pwd  
  58.         cmd.Parameters.Add(Param)  
  59.   
  60.         Param = New SqlParameter("@User_ID", SqlDbType.VarChar)  
  61.         Param.Value = modelUser.User_Pwd  
  62.         cmd.Parameters.Add(Param)  
  63.         Try  
  64.             conn.Open()  
  65.             user_Pwd = cmd.ExecuteScalar.ToString  
  66.         Catch ex As Exception  
  67.             MsgBox(ex.Message)  
  68.             Throw New Exception(ex.Message)  
  69.         Finally  
  70.             conn.Close()  
  71.             cmd.Dispose()  
  72.             cmd = Nothing  
  73.         End Try  
  74.         Return user_Pwd  
  75.     End Function  
  76.     ''' <summary>  
  77.     ''' 读取用户身份级别  
  78.     ''' </summary>  
  79.     ''' <param name="modelUser">用户实体类</param>  
  80.     ''' <returns>身份级别</returns>  
  81.     ''' <remarks></remarks>  
  82.     Public Function GetLevel(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetLevel  
  83.         Dim User_Level As String  
  84.         Dim spName As String  
  85.         spName = "proc_GetUserLevel"  
  86.         Dim conn As New SqlConnection(sqlHelp.connStr)  
  87.         Dim cmd As New SqlCommand(spName, conn)  
  88.         cmd.CommandType = CommandType.StoredProcedure  
  89.   
  90.         Dim Param As SqlParameter  
  91.         Param = New SqlParameter("@User_ID", SqlDbType.VarChar)  
  92.         Param.Value = modelUser.User_ID  
  93.         cmd.Parameters.Add(Param)  
  94.         Try  
  95.             conn.Open()  
  96.             User_Level = cmd.ExecuteScalar.ToString  
  97.         Catch ex As Exception  
  98.             MsgBox(ex.Message)  
  99.             Throw New Exception(ex.Message)  
  100.         Finally  
  101.             conn.Close()  
  102.             cmd.Dispose()  
  103.             cmd = Nothing  
  104.         End Try  
  105.         Return User_Level  
  106.     End Function  
  107. End Class  

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

  1. Imports Model  
  2. Imports IBLL  
  3. Imports System.Collections.Generic  
  4. Imports BLLFactory  
  5. Public Class frmLogIn  
  6.   
  7.     Private bllFactory As New BLLFactory.CreateBLL  
  8.     Private Sub btnLogIn_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles btnLogIn.Click  
  9.         Dim modelUser As New Model.User  
  10.         Dim userLevel As String  
  11.   
  12.         modelUser.User_ID = txtUserID.Text  
  13.         modelUser.User_Pwd = txtUserPwd.Text  
  14.   
  15.   
  16.         '通过业务逻辑工厂指定业务逻辑接口要使用的具体的业务逻辑层中的具体类  
  17.         userLevel = bllFactory.CreateUserBLL.LogIn(modelUser)  
  18.   
  19.   
  20.         Select Case userLevel  
  21.             Case "一般用户"  
  22.   
  23.             Case "操作员"  
  24.   
  25.             Case "管理员"  
  26.                 frmMDIParentForm.Show()  
  27.               
  28.             Case Else  
  29.                 MsgBox("未知错误!")  
  30.                 Exit Sub  
  31.         End Select  
  32.     End Sub  
  33.   
  34.     Private Sub btnExit_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles btnExit.Click  
  35.         txtUserID.Text = ""  
  36.         txtUserPwd.Text = ""  
  37.         Me.Close()  
  38.     End Sub  
  39.      
  40. End Class  

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

最后说说我自己的感触。

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

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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:671213次
    • 积分:12478
    • 等级:
    • 排名:第1170名
    • 原创:288篇
    • 转载:1964篇
    • 译文:3篇
    • 评论:10条
    最新评论