从开始画UML图就一直听说三层,当时就查过一些资料有个大概的理解,现在到了真正学习三层的时候当然要理清楚思路。
三层的存在一定是有它的道理,那么使用三层来设计程序有什么好处,答案是“高内聚,低耦合”这该如何理解呢?那就先理解一下内聚和耦合。内聚就是说一个模块在完成职责时应该尽量的只和自己内部的元素联系,不要麻烦其他的模块。耦合则刚好是相反的一个模块在完成自己职责时,不得不调用其他的模块,这就是耦合,用来描述两个模块联系程度的。
我们在设计软件时一定要做到高内聚,低耦合,这样有利于维护和修改。而三层就是为了跟好的满足,自己是这样理解的,每一层将自己封装起来,和其他层打交道时都是通过方法或函数,这对其它层来说就是接口,在日后进行维护或修改的时候“接口”是保持不变的,层内部进行改变,并不影响其它层的他调用,无需修改。
接下来就是对三层的初步认识。三层包括:UI、BLL、DAL。那么具体的再看各层的作用
UI
显示数据;收集信息和操作指令
BLL
接受UI数据和指令,执行业务逻辑;
如果需要使用到DB则调用DAL;
将DAL的数据返回给UI显示
DAL
和数据源打交道,增、删、改、查。
他们之间的关系
U调B,B调D,D连数据源;D返B,B返U,U显示。
来看一个具体登入的例子
一个程序一共包括4个项目,分别是UI、BLL、DAL和Model。前三者不用说就是三层的每一层,而Model是一个数据模型查了一些资料这个东西可以理解为数据库的一个代表,它里面的属性就是数据库里的字段,它的作用是传递数据使用的,可以被三层中的每一层都引用但是它不知道三层的存在。具体的好处还不是很理解。
接着看每层都是如何工作的呢。就有外到内依次深入。首先是UI
UI和我们学过的VB很相似,一个WinForms,在上面拖拽一些控件,双击控件就可以打开编写。
登入按钮双击开始编写代码,在原来VB没有用三层的时候我们是直接编写程序打开数据库进行验证、登录。而在三层里UI是不可以直接和DB进行接触的,它必须通过BLL层来进行“验证”这一业务的实现。
- Private SubcmdButn_Click(sender As Object,e As EventArgs)HandlescmdButn.Click
- '实例化一个B层的类
- Dimmanager As Login.BLL.loginManager= New BLL.loginManager()
- '定义一个实体,给其属性赋值,并将这的实例化的实体作为参数传给B层
- DimtmpUser As NewLogin.Model.UserModel
- tmpUser.Name = txtUser.Text.Trim()
- tmpUser.passWord = txtPassWord.Text
- tmpUser.Level = "Manager"
- tmpUser.WorkData = Convert.ToString(Today())
- tmpUser.WorkTime = Convert.ToString(TimeOfDay())
- tmpUser.OverData = ""
- tmpUser.OverTime = ""
- '调用B层的方法,将赋了值的实例化的实体tmpUser作为参数传递给调用的函数
- manager.userLogin(tmpUser)
- MessageBox.Show("登入用户:" +tmpUser.Name)
BLL实现具体的“验证”业务,分为两种情况,如果B层自己能够完成U层的要求,则自己解决,如果需要数据才能完成,则需要调用D层连接数据库完成。
- Public FunctionuserLogin(ByVal UIuser AsLogin.Model.UserModel) AsModel.UserModel
- '定义一个D层类的对象
- DimsUser As Login.DAL.selectUser= New DAL.selectUser
- '定义实体类的对象,用来接收D层返回的结果
- Dimuser As Login.Model.UserModel
- '该语句是一个桥梁,接收U层传来的实体参数,同时接收D层SelectUser函数返回的结果
- user= sUser.SelectUser(UIuser)
- '这一段用来判断是否有符合的结果,如果有就记录该用户
- If(Not IsDBNull(user)) Then
- '定义一个D层的UpdateRecord类
- Dimupuser As Login.DAL.updataRecord= New DAL.updataRecord()
- '桥梁,将UI层的参数传来的实体当参数,对数据库进行更新
- upuser.upWorkRecord(UIuser)
- EndIf
- Returnuser '返回user给U层
- End Function
- Public SharedFunctiongetConnection() As sqlConnection
- '因为Connection是上所有打开数据的时候都需要用到的部分,所以可以写为函数调用
- Dimstrconn As String= "Server=localhost;Database=Users;UID=sa;PWD=123456"
- Dimconn As sqlConnection= New sqlConnection(strconn)
- Returnconn
- End Function
- Public FunctionSelectUser(ByVal BLLuser AsLogin.Model.UserModel) AsModel.UserModel
- Dimcn As sqlConnection= DBConn.getConnection() '调用写好的链接函数
- '定义具体的sql语句
- Dimsql As String= "select userID,Level,UserName from UserInfo where userID=@UserNameand PWD=@PassWord"
- Dimcmd As sqlCommand= New sqlCommand(sql,cn) '执行Command命令
- '给Command添加属性,这时使用实体的属性值来把参数传给sql中的变量
- cmd.Parameters.Add(New sqlParameter("@UserName",BLLuser.Name))
- cmd.Parameters.Add(New sqlParameter("@PassWord",BLLuser.passWord))
- cn.Open()
- '定义DataReader用例存储查找到的结果,并且一条一条的进行读取
- Dimreader As sqlDataReader= cmd.ExecuteReader
- Dimuser As Model.UserModel= New Model.UserModel
- While(reader.Read)
- If(IsDBNull(user)) Then
- user = New Model.UserModel
- EndIf
- user.Name = reader.ToString(0)
- user.passWord = reader(2)
- EndWhile
- cn.Close()
- Returnuser '返回结果给B层
- End Function
- Public SubupWorkRecord(ByVal bllUser AsLogin.Model.UserModel)
- Usingcn As sqlConnection= DBConn.getConnection()
- Dimsql As String= "Insert IntoWorkRecord(UserName,WorkDate,WorkTime,OverData,OverTime) values(@UserName,@Level,@WorkDate,@WorkTime,@OverData,@OverTime)"
- Dimcmd As sqlCommand= New sqlCommand(sql,cn)
- '这里接收的参数来自B层的
- cmd.Parameters.Add(New sqlParameter("@UserName",bllUser.Name))
- cmd.Parameters.Add(New sqlParameter("@Level",bllUser.Level))
- cmd.Parameters.Add(New sqlParameter("@WorkDate",bllUser.WorkData))
- cmd.Parameters.Add(New sqlParameter("@WorkTime",bllUser.WorkTime))
- cmd.Parameters.Add(New sqlParameter("@OverData",bllUser.OverData))
- cmd.Parameters.Add(New sqlParameter("@OverTime",bllUser.OverTime))
- cn.Open()
- cmd.ExecuteNonQuery() '执行更新
- '因为使用Using方法,在使用完毕后自动关闭,所以不需要调用Close()
- EndUsing
用Rose画了一张图用来帮助理解,有些可能不符合UML的要求,只是用来帮助理解三层。
当用户在登录窗体中click登录之后,U调用B的userLogin() 方法,以为B无法自己独立完成验证,所以调用D层的selectUser() 连接数据源进行验证。