- 认识分层:
做开发也有几年了,在开发软件的过程中,分层式结构是最常见的,也是非常重要的。
其实,分层只是一种思想,与层数无关,不管是分为三层,还是五层,抑或是七层,这些分法都是为了让我们的项目适应变化,可重用,方便程序员的开发。
那些所谓的代名词:
表示层:这一层是给用户的界面;就是和用户交互的,用于显示和接收用户输入的数据的。
数据层:这一层就是负责数据库的访问,其实就是对数据表的增删改查等操作。
业务逻辑层:这一层处于表示层和数据层中间,可以看出这一层的重要性。我给他起了另外一个名字叫“华丽的承上启下”。这一层主要完成业务的规则定制、业务流程实现等任务。
各司其职,分层治之:
我们将整个项目分层,这样可以做到各司其职,各管各家,分开治之。
我们的开发人员可以只关注更个结构中的其中一层,其余的都可以不用去照顾,这样每个成员都有自己的关注点,使分工很明确。
维护也就显得非常容易,只要用新的实现替换原有层次的实现即可;
分层治之,可以让层层之间依赖性降低,达到解耦的目的。
我们也很容易的发现,数据层的代码复用率高,而且分层之后各层之间的逻辑也得到了复用。
我们在开发的时候也可以用多语言进行开发。
层,要合理的去分:
分层就像设计模式一样,要想用好不是容易的事情,我也是正在摸索中,在项目中积累经验。
但是我知道,层不是说分三层就分三层,分五层就五层,七层就七层的,要看具体的项目工程。一如设计模式,不是说看懂了这个设计模式,我做项目中一定要用到它;用不好有时候会吃亏的。
项目实战--分一回:
rose架构图:
表示层代码:
'''
''' 单击确定按钮
'''
'''
'''
''' 2011-3-20 13:21 by lfsf802
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.ClickIf Trim(txtUsername.Text) = "" Or Trim(txtPwd.Text) = "" Or Trim(cmbStatus.Text) = "" ThenMsgBox("信息不能为空")
wrongReturn()Exit SubEnd IfIf blluserInfo.checkRecord(Trim(txtUsername.Text), Trim(txtPwd.Text), Trim(cmbStatus.Text)) ThenfrmMain.show()Else
MsgBox("输入信息错误,请您重新输入")
wrongReturn()Return
End IfEnd Sub
业务逻辑层代码:
'''
''' 判断登录
'''
''' 用户名
''' 密码
''' 身份
''' 是否登录成功
''' 2011-3-20 15:14 by lfsf802
Public Function checkRecord(ByVal userName As String, ByVal password As String, ByVal status As String) As BooleanentityUserInfo.UserName = userNameDim entityuser As New Entity.UserInfoiuserinfo = dataaccess.CreateUserInfoentityuser = iuserinfo.ReturnRecord(entityUserInfo)If entityuser.Password = password And entityuser.StatusType = status ThenReturn TrueElse
Return FalseEnd IfEnd Function'''
''' 返回一条用户信息记录
'''
''' 一条用户信息
''' 一条用户信息记录
''' 2011-3-19 16:08 by lfsf802
Public Function ReturnRecord(ByVal entityUserInfo As Entity.UserInfo) As Entity.UserInfoiuserinfo = dataaccess.CreateUserInfoReturn iuserinfo.ReturnRecord(entityUserInfo)
End Function
数据层:
这里用了抽象工厂设计模式,并且用到了反射技术和配置文件来辅助抽象工厂。
'===============================================================
'这个是用反射+抽象工厂的数据库访问方式,我们考虑的是可以不在程序
'写具体让实例化那个类,而是根据db的值来去某个地方找应该实例化哪个
'类,这样就和select说再见了。
'用到反射技术,将greateInstance里面的变量值初始化成想要的类,而实
'例化的依据就是db变量来决定的。这样就除去了switch判断的麻烦。
'================================================================
Private ReadOnly AssemblyName As String = "DAL"'这是反射+配置文件实现数据库访问方式,这样我们只要在配置文件中写明
'是哪个数据库就ok了,连dataAccess类也不用更改了
Private ReadOnly db As String = System.Configuration.ConfigurationSettings.AppSettings("DB")'Private ReadOnly db As String = "sql"
'''
''' 实例化用户表类
'''
''' 用户表类
''' 2011-3-23 9:57 by cjq
Public Function CreateUserInfo() As IUserInfoDim className As String = AssemblyName + "." + db + "UserInfo"Return CType(Assembly.Load(AssemblyName).CreateInstance(className), IUserInfo)End Function
接口层:
'''
''' 检查一条记录
'''
''' 一条记录
''' 是否存在这样一条记录
''' 2011-3-19 14:56 by cjq
Function checkRecord(ByVal entityUserInfo As Entity.UserInfo) As Boolean'''
''' 返回一条记录
'''
''' 一条记录
''' 一条用户记录
''' 2011-3-19 14:56 by cjq
Function ReturnRecord(ByVal entityUserInfo As Entity.UserInfo) As Entity.UserInfo
数据持久层(在这里笔者参数传递可以换成存储过程):
在这里小论一下存储过程:
在程序逻辑中写SQL语句是很难处理复杂业务逻辑的,困难的引号匹配,复杂的字符串拼接过程,以及字符串拼接过程中的效率损失是我们不得不考虑的。
于是,处理复杂逻辑,如复杂的事务处理时,存储过程几乎成了唯一的方法。但是,存储过程一样有着他的致命缺点,就是他的移植性很差:当我们的项目更换数据库时,我们就要把存储过程完整地去拷贝到另外一个数据库,然后考虑他们的兼容问题;当我们修改数据层访问代码时,我们需要动的不再是应用程序服务器的代码,而是数据库端的代码,这是稍显痛苦的地方。(还是那句,没有完美的,只有最合适的)
'''
''' 检查一条记录
'''
''' 一记录
''' 是否存在这样一条记录
''' 2011-3-19 20:32 by lfsf802
Public Function checkRecord(ByVal entityUserInfo As Entity.UserInfo) As Boolean Implements Interfaces.IUserInfo.checkRecordDim sql As String = "select * from UserInfo where userName=@userName"Dim conn As SqlConnection = New SqlConnection(connStr)Dim cmd As SqlCommand = New SqlCommand(sql, conn)Dim sqlParam As SqlParametersqlParam = New SqlParameter("@userName", SqlDbType.VarChar)sqlParam.Value = entityUserInfo.UserNamecmd.Parameters.Add(sqlParam)Dim sdr As SqlDataReader = NothingTry
conn.Open()sdr = cmd.ExecuteReader()sdr.Read()If (sdr.HasRows = True) ThenReturn TrueElse
Return FalseEnd If'Return cmd.ExecuteNonQuery() > 0
Catch ex As ExceptionReturn FalseFinally
If Not IsNothing(conn) Thenconn.Close()conn = Nothing
End IfIf Not IsNothing(cmd) Thencmd.Dispose()cmd = Nothing
End IfEnd TryEnd Function'''
''' 返回一条用户信息记录
'''
''' 一条用户信息
''' 一条用户信息记录
''' 2011-3-19 16:06 by lfsf802
Public Function ReturnRecord(ByVal entityUserInfo As Entity.UserInfo) As Entity.UserInfo Implements Interfaces.IUserInfo.ReturnRecordDim sql As String = "select * from UserInfo where userName=@userName"Dim conn As SqlConnection = New SqlConnection(connStr)Dim cmd As SqlCommand = New SqlCommand(sql, conn)Dim sqlParam As SqlParametersqlParam = New SqlParameter("@userName", SqlDbType.VarChar)sqlParam.Value = entityUserInfo.UserNamecmd.Parameters.Add(sqlParam)Dim sdr As SqlDataReader = NothingTry
conn.Open()sdr = cmd.ExecuteReader()While sdr.Read
entityUserInfo.UserName = Trim(sdr.GetString(0))entityUserInfo.Password = Trim(sdr.GetString(1))entityUserInfo.StatusType = Trim(sdr.GetString(2))entityUserInfo.ComputerLabNo = Trim(sdr.GetInt32(3))entityUserInfo.TeacherName = Trim(sdr.GetString(4))End WhileReturn entityUserInfo
Catch ex As ExceptionReturn NothingFinally
If Not IsNothing(conn) Thenconn.Close()conn = Nothing
End IfIf Not IsNothing(cmd) Thencmd.Dispose()cmd = Nothing
End IfEnd TryEnd Function
实体层(mapping数据库):
Public Class UserInfoPrivate _intComputerLabNo As IntegerPrivate _strPassword As StringPrivate _strStatusType As StringPrivate _strTeacherName As StringPrivate _strUserName As String'''
''' 机房号
'''
'''
'''
''' 2011-3-14 15:26 by lfsf802
Public Property ComputerLabNo() As IntegerGet
Return _intComputerLabNo
End GetSet(ByVal value As Integer)_intComputerLabNo = valueEnd SetEnd Property'''
''' 用户名
'''
'''
'''
''' 2011-3-14 15:27 by lfsf802
Public Property UserName() As StringGet
Return _strUserName
End GetSet(ByVal value As String)_strUserName = valueEnd SetEnd Property'''
''' 口令密码
'''
'''
'''
''' 2011-3-14 15:28 by lfsf802
Public Property Password() As StringGet
Return _strPassword
End GetSet(ByVal value As String)_strPassword = valueEnd SetEnd Property'''
''' 身份类型
'''
'''
'''
''' 2011-3-14 15:29 by lfsf802
Public Property StatusType() As StringGet
Return _strStatusType
End GetSet(ByVal value As String)_strStatusType = valueEnd SetEnd Property'''
''' 教师姓名
'''
'''
'''
''' 2011-3-14 15:30 by lfsf802
Public Property TeacherName() As StringGet
Return _strTeacherName
End GetSet(ByVal value As String)_strTeacherName = valueEnd SetEnd PropertyEnd Class
飞了一回,你飞了吗?
分层,到这里就唠叨完了,不知道读者对三层有所了解?可以自己做一个小的demo试试哦!
分层,其实没有那么困难,关键是看思维能不能打开,能不能让自己飞起来,相信自己,飞一回试试!!!
最后:
没有完美的分层,只有适合项目工程的分层;但是你是完美的,要相信自己!