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

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

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

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

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

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

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

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

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

Imports System.Configuration
Imports System.Reflection
Public Class DependencyInjector

    ''' <summary>
    ''' 利用反射机制,取得数据访问层对象
    ''' </summary>
    ''' <param name="className">传入数据访问层中要实例化的类的名称</param>
    ''' <returns>指定的数据访问层的类</returns>
    ''' <remarks></remarks>
    Public Function GetDALObject(ByVal className As String) As Object
        Dim dal As Object
        Dim dalName As String
        Dim fullClassName As String
        Dim dalObj As Object

        '通过配置文件的指定要应用的DAL层
        dal = System.Configuration.ConfigurationManager.AppSettings("DAL")

        'dalName就是常说的用用程序的名称
        dalName = dal.ToString

        '命名空间+类的名称,指明要实例化的类的路径
        fullClassName = dalName + "." + className
        '通过反射,取得数据访问层对象
        dalObj = Assembly.Load(dalName).CreateInstance(fullClassName)
        '返回指定的对象
        Return dalObj

    End Function


    ''' <summary>
    '''取得指定业务逻辑层的指定类
    ''' </summary>
    ''' <param name="className">要应用的业务逻辑层的具体类的名称</param>
    ''' <returns>指定的业务逻辑层的类(对象)</returns>
    ''' <remarks></remarks>
    Public Function GetBLLObject(ByVal className As String) As Object

        Dim bll As Object
        Dim bllName As String
        Dim bllObj As Object
        Dim fullClassName As String

        '从配置文件中读取业务逻辑名称
        bll = System.Configuration.ConfigurationManager.AppSettings("BLL")
        bllName = bll.ToString

        fullClassName = bllName + "." + className

        '利用反射取得业务逻辑层对象
        bllObj = Assembly.Load(bllName).CreateInstance(fullClassName)

        Return bllObj

    End Function
End Class

2、相关配置文件:

<appSettings>
    <add key="connStr"  value="Persist Security Info=true;Data Source=*****;Initial Catalog=Charge_Sys_SelfDesign;User ID=sa;PWD=****;" />
    <add key="DAL"  value="DAL" />
    <add key="BLL"  value="BLL" />    
  </appSettings>

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

 <add key="DAL"  value="AccessDAL" />

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

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

Imports IBLL
Imports Utility
Imports System.Configuration
Imports System.Reflection
Imports System.Windows.Forms
Public Class CreateBLL

    Private dependecy As New Utility.DependencyInjector

    ''' <summary>
    ''' 生成用户业务逻辑层访问对象
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CreateUserBLL() As IBLL.IUserBLL
        Try
            '如果不用注入,需要重复N次BLL,
            'Return CType(Assembly.Load("BLL").CreateInstance("BLL.UserBLL"),IBLL.IUserBLL)
            '优化后只需指明具体类名,如果要换层,只需到配置文件中做简单修改即可,程序代码清晰,不宜出错。
            Return CType(dependecy.GetBLLObject("UserBLL"),IBLL.IUserBLL)
        Catch ex As Exception
            MsgBox(ex.Message)
            Return Nothing
        End Try
    End Function
End Class

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

Imports Utility
Imports System.Configuration
Imports System.Reflection
Imports IDAL
Imports System.Windows.Forms

Public Class CreateDAL

    Private dependency As New Utility.DependencyInjector

    ''' <summary>
    ''' 生成用户指定的数据访问层对象
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CreateUserDAL() As IDAL.IUserDAL
        Try
            'Return CType(Assembly.Load("DAL").CreateInstance("DAL.UserDAL"),IDAL.IUserDAL)
            Return CType(dependency.GetDALObject("UserDAL"),IDAL.IUserDAL)
        Catch ex As Exception
            MessageBox.Show(ex.Message)
            Return Nothing
        End Try

    End Function
End Class

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

Public Interface IUserBLL

    '返回用户登录的身份级别,在接口的实现中要验证用户名和密码
    Function LogIn(ByVal modelUser As Model.User) As String

End Interface

6、数据访问层接口:

Public Interface IUserDAL

    '获取用户ID
    Function GetID(ByVal modelUser As Model.User) As String
    '获取用户密码
    Function GetPwd(ByVal modelUser As Model.User) As String
    '获取用户级别
    Function GetLevel(ByVal modelUser As Model.User) As String

End Interface

7、业务逻辑层:

Imports DALFactory
Imports IBLL
Imports IDAL
Imports Model
Imports System.Data.SqlClient
Imports System.Collections.Generic
Public Class UserBLL
    Implements IBLL.IUserBLL
    Private dalFactory As New DALFactory.CreateDAL
    ''' <summary>
    ''' 先判断用户名和密码是否正确,然后返回用户级别
    ''' </summary>
    ''' <param name="modelUser">用户实体类</param>
    ''' <returns>用户级别</returns>
    ''' <remarks></remarks>
    Public Function LogIn(ByVal modelUser As Model.User) As String Implements IBLL.IUserBLL.LogIn
        Dim userLevel As String
        Try
            If dalFactory.CreateUserDAL.GetID(modelUser) = "" Then
                MsgBox("用户名错误!")
                Return Nothing
                Exit Function
            End If
            If dalFactory.CreateUserDAL.GetPwd(modelUser) = "" Then
                MsgBox("密码名错误!")
                Return Nothing
                Exit Function
            End If


            '通过数据访问层工厂指定实现数据访问层接口的具体的数据访问层以及该层的具体类
            userLevel = dalFactory.CreateUserDAL.GetLevel(modelUser)


        Catch ex As Exception
            Return Nothing
        End Try
        Return userLevel
    End Function
End Class

?

8、数据访问层:

Imports System.Data.SqlClient
Imports Utility
Imports System.Windows.Forms

Public Class UserDAL
    '实现数据访问层的接口
    Implements IDAL.IUserDAL
    Private sqlHelp As New Utility.SQLServerDALHelp
    ''' <summary>
    ''' 读取用户ID
    ''' </summary>
    ''' <param name="modelUser">用户实体类</param>
    ''' <returns>用户ID</returns>
    ''' <remarks></remarks>
    Public Function GetID(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetID
        Dim User_ID As String
        Dim conn As New SqlConnection(sqlHelp.connStr)
        Dim spName As String
        spName = "proc_GetUserID"
        Dim cmd As New SqlCommand(spName,conn)
        cmd.CommandType = CommandType.StoredProcedure
        Dim Param As SqlParameter
        Param = New SqlParameter("@User_ID",SqlDbType.VarChar)
        Param.Value = modelUser.User_ID
        cmd.Parameters.Add(Param)

        Try
            conn.Open()
            User_ID = cmd.ExecuteScalar.ToString
            Return User_ID
        Catch ex As Exception
            MsgBox(ex.Message)
            Throw New Exception(ex.Message)
        Finally
            conn.Close()
            cmd.Dispose()
            cmd = Nothing
        End Try
        Return User_ID
    End Function
    ''' <summary>
    ''' 读取用户密码
    ''' </summary>
    ''' <param name="modelUser">用户实体类</param>
    ''' <returns>密码</returns>
    ''' <remarks></remarks>
    Public Function GetPwd(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetPwd
        Dim user_Pwd As String
        Dim spName As String
        spName = "proc_GetUserPwd"
        Dim conn As New SqlConnection(sqlHelp.connStr)
        Dim cmd As New SqlCommand(spName,conn)
        cmd.CommandType = CommandType.StoredProcedure

        Dim Param As SqlParameter
        Param = New SqlParameter("@User_Pwd",SqlDbType.VarChar)
        Param.Value = modelUser.User_Pwd
        cmd.Parameters.Add(Param)

        Param = New SqlParameter("@User_ID",SqlDbType.VarChar)
        Param.Value = modelUser.User_Pwd
        cmd.Parameters.Add(Param)
        Try
            conn.Open()
            user_Pwd = cmd.ExecuteScalar.ToString
        Catch ex As Exception
            MsgBox(ex.Message)
            Throw New Exception(ex.Message)
        Finally
            conn.Close()
            cmd.Dispose()
            cmd = Nothing
        End Try
        Return user_Pwd
    End Function
    ''' <summary>
    ''' 读取用户身份级别
    ''' </summary>
    ''' <param name="modelUser">用户实体类</param>
    ''' <returns>身份级别</returns>
    ''' <remarks></remarks>
    Public Function GetLevel(ByVal modelUser As Model.User) As String Implements IDAL.IUserDAL.GetLevel
        Dim User_Level As String
        Dim spName As String
        spName = "proc_GetUserLevel"
        Dim conn As New SqlConnection(sqlHelp.connStr)
        Dim cmd As New SqlCommand(spName,conn)
        cmd.CommandType = CommandType.StoredProcedure

        Dim Param As SqlParameter
        Param = New SqlParameter("@User_ID",SqlDbType.VarChar)
        Param.Value = modelUser.User_ID
        cmd.Parameters.Add(Param)
        Try
            conn.Open()
            User_Level = cmd.ExecuteScalar.ToString
        Catch ex As Exception
            MsgBox(ex.Message)
            Throw New Exception(ex.Message)
        Finally
            conn.Close()
            cmd.Dispose()
            cmd = Nothing
        End Try
        Return User_Level
    End Function
End Class

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

Imports Model
Imports IBLL
Imports System.Collections.Generic
Imports BLLFactory
Public Class frmLogIn

    Private bllFactory As New BLLFactory.CreateBLL
    Private Sub btnLogIn_Click(ByVal sender As System.Object,ByVal e As System.EventArgs) Handles btnLogIn.Click
        Dim modelUser As New Model.User
        Dim userLevel As String

        modelUser.User_ID = txtUserID.Text
        modelUser.User_Pwd = txtUserPwd.Text


        '通过业务逻辑工厂指定业务逻辑接口要使用的具体的业务逻辑层中的具体类
        userLevel = bllFactory.CreateUserBLL.LogIn(modelUser)


        Select Case userLevel
            Case "一般用户"

            Case "操作员"

            Case "管理员"
                frmMDIParentForm.Show()
            
            Case Else
                MsgBox("未知错误!")
                Exit Sub
        End Select
    End Sub

    Private Sub btnExit_Click(ByVal sender As System.Object,ByVal e As System.EventArgs) Handles btnExit.Click
        txtUserID.Text = ""
        txtUserPwd.Text = ""
        Me.Close()
    End Sub
   
End Class

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

最后说说我自己的感触。

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

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

(编辑:李大同)

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

    推荐文章
      热点阅读