解析三层架构(2)----分层究竟分出了那些东西

来源:互联网 发布:snmp使用的端口号是 编辑:程序博客网 时间:2024/04/29 13:19

在上篇文章写到我们为什么要分层.有很多读者提出来很多宝贵的意见.让我受益匪浅,深深的感觉到自己的水平"还有很大的提升空间".首先感谢这些朋友们,我会进一步总结完善自己的想法.

截取了部分朋友的留言,感谢他们:

clip_image001

clip_image002

clip_image003

这次我用对比的方式描述一下,分层到底分出了什么.俗话说:有分必有合,那么它是把什么合到了一起.

首先写出两个没有分层的demo:

<1>查询信息demo

   1: Public Class Form2
   2:     Private sqlCon As String = "Data Source=LSH;Initial Catalog=ComputerLab;User ID=sa;Password=123456"
   3:     '查询数据库信息信息
   4:     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   5:  
   6:         Dim conStr As New SqlConnection '数据库连接对象
   7:         Dim sqlCom As SqlCommand        '数据库执行对象
   8:         Dim Res As DialogResult         '消息提示框返回类型
   9:         Dim dr As SqlDataReader         'dataReader对象
  10:         Dim dt As New DataTable         'Datatable对象
  11:  
  12:         Dim sql As String = "select * from TableName where Name=@name"                   'sql插入语句
  13:         conStr.ConnectionString = sqlCon                                '给数据库连接对象赋值
  14:         sqlCom = New SqlCommand(sql, conStr)                            '给数据库执行对象赋值
  15:         sqlCom.Parameters.Add("@name", SqlDbType.VarChar, TextBox1.Text)    '给sql语句参数赋值
  16:  
  17:         Res = MessageBox.Show("是否添加", "提示", MessageBoxButtons.OKCancel)
  18:         '判断是否查询
  19:         If Res = DialogResult.Yes Then
  20:             Try
  21:                 conStr.Open()
  22:                 dr = sqlCom.ExecuteReader    '执行查询语句
  23:                 dt.Load(dr)
  24:             Catch ex As Exception
  25:                 Throw ex
  26:             Finally
  27:                 If Not IsNothing(conStr) Then   '如果数据库打开,则关闭数据库
  28:                     conStr.Close()
  29:                 End If
  30:             End Try
  31:         End If
  32:  
  33:         MsgBox(dt.Rows.Count)   '显示查询到的行数
  34:     End Sub
 
<2>添加信息demo
   1: Public Class Form1
   2:     Private sqlCon As String = "Data Source=LSH;Initial Catalog=ComputerLab;User ID=sa;Password=123456"
   3:     '向数据库添加信息
   4:     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   5:         Dim bln As Boolean = False      '存储返回值
   6:         Dim conStr As New SqlConnection '数据库连接对象
   7:         Dim sqlCom As SqlCommand        '数据库执行对象
   8:         Dim Res As DialogResult         '消息提示框返回类型
   9:  
  10:         Dim sql As String = "insert into TableName(Name) value(@Name)"  'sql插入语句
  11:         conStr.ConnectionString = sqlCon                                '给数据库连接对象赋值
  12:         sqlCom = New SqlCommand(sql, conStr)                            '给数据库执行对象赋值
  13:         sqlCom.Parameters.Add("@Name", SqlDbType.VarChar, TextBox1.Text) '给sql语句参数赋值
  14:  
  15:         Res = MessageBox.Show("是否添加", "提示", MessageBoxButtons.OKCancel)
  16:         '判断是否同意插入
  17:         If Res = DialogResult.Yes Then
  18:             Try
  19:                 conStr.Open()
  20:                 bln = sqlCom.ExecuteNonQuery    '执行插入语句
  21:             Catch ex As Exception
  22:                 bln = False
  23:             Finally
  24:                 If Not IsNothing(conStr) Then   '如果数据库打开,则关闭数据库
  25:                     conStr.Close()
  26:                 End If
  27:             End Try
  28:         End If
  29:  
  30:         MsgBox(bln) '提示返回信息
  31:     End Sub
  32: End Class

上面的两个没有分层的代码,当然在功能的实现上是完全没有问题.但是这样设计带来了很多的危机:

(1):写这个模块的人必须是一个能力非常强的人,因为他需要操作数据库,理解业务逻辑

(2):如果有另为一个模块需要这两个操作数据库的方法,只能重写或者调用该窗体下的方法.

(3):如果业务逻辑稍有变动,就需要拆开这个模块,重新编写,对于重新设计的人来说这个模块的所有设计都是可见的,很是不安全.

(4):如果要更换或者改动数据库,结果就是每一个模块都要重新编写

(5):如果有另为一个和这个相似的工程,想要复用以前的东西,难度很大

对于一个公司来说,当然不希望发生这样的事情.所以,合理的分层,降低系统的耦合性是必要的.

第一步:把数据库操作提取出来.

对每一个表,把完全对他的操作单独写成类也就是我们的DAO,数据访问类

每一个表对应一个DAO,每个DAO里面包含该数据库的所有增,删,查,改操作.这样做的好处就是对数据库的操作完全独立,达到功能单一.降低了和其他模块的耦合,写代码的时候,程序员不需要了解业务逻辑.

而且,在维护上和扩展上,如果对数据库改动,就会有针对性的去修改DAO.

当然,在设计的时候,我们尽量去封装不变的操作,这样不但能减少代码量,而且能达到很好的复用效果.在数据库操作中,对于数据库的打开,执行,关闭基本上都是一样的.所以我们单独提取出来sqlHelper(数据库助手类)

 

   1: Imports System.Data.SqlClient
   2: Imports System.Configuration
   3:  
   4: ''' <summary>
   5: ''' 运行数据库
   6: ''' </summary>
   7: Public Class SqlHelp
   8:     Private cmd As SqlCommand   'sqlcommand对象
   9:     Private con As SqlConnection    'sqlconnection对象
  10:     Private dr As SqlDataReader     'sqlDataReader对象
  11:     ''' <summary>
  12:     ''' 获取连接字符串
  13:     ''' </summary>
  14:     ''' <returns>连接字符串</returns>
  15:     ''' <remarks></remarks>
  16:     Public Function GetCon() As SqlConnection
  17:         Dim conStr As String
  18:         conStr = System.Configuration.ConfigurationManager.AppSettings("ConnStr")
  19:  
  20:         con = New SqlConnection
  21:         con.ConnectionString = conStr
  22:  
  23:         '打开数据库
  24:         If (con.State = ConnectionState.Closed) Then
  25:             con.Open()
  26:         End If
  27:  
  28:         Return con
  29:  
  30:     End Function
  31:  
  32:     ''' <summary>
  33:     ''' 增 删 改方法
  34:     ''' </summary>
  35:     ''' <param name="sqlStr">数据库语句 活存储过程名</param>
  36:     ''' <param name="Param">参数数组</param>
  37:     ''' <param name="commandType">数据库字符串 或者存储过程名 类型</param>
  38:     Public Function ExecuteNonQuery(ByVal sqlStr As String, ByVal Param() As SqlParameter, ByVal commandType As CommandType) As Boolean
  39:  
  40:         cmd = New SqlCommand(sqlStr, Me.GetCon)
  41:         cmd.CommandType = commandType
  42:  
  43:         '添加参数
  44:         If Param IsNot Nothing Then
  45:             cmd.Parameters.AddRange(Param)
  46:         End If
  47:  
  48:         '执行语句
  49:         Try
  50:             Return CBool(cmd.ExecuteNonQuery)
  51:         Catch ex As Exception
  52:             Return False
  53:         Finally
  54:             If Not IsNothing(con) Then
  55:                 con.Close()
  56:             End If
  57:         End Try
  58:  
  59:     End Function
  60:  
  61:     ''' <summary>
  62:     ''' 查询
  63:     ''' </summary>
  64:     ''' <param name="sqlStr">sql语句 或者存储过程名</param>
  65:     ''' <param name="Param">参数数组</param>
  66:     ''' <param name="commandType">执行类型</param>
  67:     Public Function ExecuteQuery(ByVal sqlStr As String, ByVal Param() As SqlParameter, ByVal commandType As CommandType) As DataTable
  68:         Dim dt As New DataTable
  69:         cmd = New SqlCommand(sqlStr, Me.GetCon)
  70:         cmd.CommandType = commandType
  71:  
  72:         '添加参数
  73:         If Param IsNot Nothing Then
  74:             cmd.Parameters.AddRange(Param)
  75:         End If
  76:  
  77:         '执行语句
  78:         Try
  79:             dr = cmd.ExecuteReader
  80:             dt.Load(dr)
  81:         Catch ex As Exception
  82:             Return Nothing
  83:         Finally
  84:             If Not IsNothing(con) Then
  85:                 con.Close()
  86:             End If
  87:         End Try
  88:         Return dt
  89:     End Function
提取后数据库访问层代码如下: 
   1: Imports SqlHelper
   2: Imports System.Data.SqlClient
   3: Public Class DAL
   4:     Private sqlHelper As New SqlHelp
   5:     ''' <summary>
   6:     ''' 查询记录
   7:     ''' </summary>
   8:     ''' <param name="strSelect">关键字</param>
   9:     ''' <returns>记录集</returns>
  10:     ''' <remarks></remarks>
  11:     Public Function SelectInfo(ByVal strSelect As String) As DataTable
  12:         'sql查询语句
  13:         Dim sql As String = "select * from TableName where Name=@name"
  14:         '向sql参数中添加实参
  15:         Dim Para() As SqlParameter = {New SqlParameter("@name", strSelect)}
  16:         '调用sqlhelper执行数据库操作
  17:         Return sqlHelper.ExecuteQuery(sql, Para, Data.CommandType.Text)
  18:     End Function
  19:     ''' <summary>
  20:     ''' 添加记录
  21:     ''' </summary>
  22:     ''' <param name="strAdd">记录信息</param>
  23:     ''' <returns>是否成功</returns>
  24:     ''' <remarks></remarks>
  25:     Public Function AddInfo(ByVal strAdd As String) As Boolean
  26:         'sql插入语句
  27:         Dim sql As String = "insert into TableName(Name) value(@Name)"
  28:         '向sql参数中添加实参
  29:         Dim Para() As SqlParameter = {New SqlParameter("@name", strAdd)}
  30:         '调用sqlhelper执行数据库操作
  31:         Return sqlHelper.ExecuteNonQuery(sql, Para, Data.CommandType.Text)
  32:     End Function
  33: End Class

第二:根据业务逻辑,个人习惯和用例相对应,把相关的逻辑判断,比如添加记录在什么情况下判断,添加前要判断记录是否存在等放到BLL层.

但是对于数据的有效性校验一般还是放在UI层,也可以有其他更好的方法,这些我在以后会总结出来.

代码如下:

   1: Public Class BLL
   2:     Private dal As New DAL
   3:     ''' <summary>
   4:     ''' 业务逻辑层,添加方法
   5:     ''' </summary>
   6:     ''' <param name="strAdd">添加信息</param>
   7:     ''' <remarks></remarks>
   8:     Public Sub Add(ByVal strAdd As String)
   9:         '定义用于存储查询结果的记录集
  10:         Dim dt As New DataTable
  11:         '执行查询,判断添加的信息是否已经存在
  12:         dt = dal.SelectInfo(strAdd)
  13:         If dt.Rows.Count > 0 Then
  14:             Throw New Exception("记录已存在")
  15:         Else
  16:             '添加记录,返回是否成功
  17:             If dal.AddInfo(strAdd) = True Then
  18:                 Throw New Exception("添加成功")
  19:             Else
  20:                 Throw New Exception("添加失败")
  21:             End If
  22:         End If
  23:  
  24:     End Sub
  25:  
  26: End Class
第三:剩下的UI层里,除了判断数据有效性以为,只要调用bll的添加方法就可以了.这样制作UI的人就会减轻了很大的压力,让他们专心做美工或者其他.

代码如下:

   1: Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
   2:         Dim bll As New BLL
   3:         '调用bll层添加方法
   4:         Try
   5:             bll.Add(TextBox1.Text)
   6:         Catch ex As Exception
   7:             '显示添加结果给用户
   8:             MessageBox.Show(ex.Message)
   9:         End Try
  10:     End Sub

当然三层方式还有其他一些技巧,比如为DAL加一层接口,用抽象工厂加反射的方法实现数据库的灵活操作,替换.这里先不叙述.

上述都是个人的一点总结,欢迎大家指导

 

接下文:解析三层架构(3)--实体类与面向对象

原创粉丝点击