本文不是从理论的角度来探讨三层架构
而是用一个示例来介绍如何建设一个三层架构的项目
并说明项目中各个文件所处的层次与作用
写本文的目的
不是为了说明自己的这个方法有多对
别人的肯定不对
而是希望给那些初学三层架构却不知从何入手的朋友提供一点帮助
因为网上的文章
大多是注重理论的介绍
而忽略了具体的实践应用
或者有示例但讲得不透彻
导致看了之后
理论上又学习了一遍
但还是不知道代码怎么写
所以想从这个方面入手写一下
让从来没做过三层架构的初学者也能照猫画虎
写出代码来
文章表述的是笔者个人对三层架构的认识
肯定有许多不足的地方
欢迎大家指正
小弟也会根据反馈来修改这篇文章
文中的代码是伪代码
仅用来阐明思路
一提三层架构大家都知道是表现层(UI)业务逻辑层(BLL)和数据访问层(DAL)而且每层如何细分也都有很多的方法但具体代码怎么写到底那些文件算在哪一层却是模模糊糊的下面用一个简单的例子来带领大家实战三层架构的项目这个例子只有一个功能就是用户的简单管理
首先建立一个空白解决方案添加如下项目及文件 添加ASPNET Web Application项目命名为UI新建Web Form类型文件Useraspx(含Useraspxcs)
添加ClassLibrary项目命名为BLL新建Class类型文件UserBLLcs
添加ClassLibrary项目命名为DAL新建Class类型文件UserDALcs添加SQLHelper引用(这个是微软的数据访问类也可以不用直接编写所有的数据访问代码我一般用自己写的数据访问类DataAccessHelper )
添加ClassLibrary项目命名为Model新建Class类型文件UserModelcs
添加ClassLibrary项目命名为IDAL新建Interface类型文件IUserDALcs
添加ClassLibrary项目命名为ClassFactory
相信大家已经看出来了这个和Petshop的示例没什么区别而且更简单因为在下也是通过Petshop学习三层架构的但一些朋友对于这几个项目所处的层次以及它们之间的关系可能比较模糊这里逐个说明一下
Useraspx和Useraspxcs 这两个文件(以及文件所属的项目下面也是如此不再重复强调了)都属于表现层部分Useraspx比较好理解因为它就是显示页面了Useraspxcs有些人觉得不应该算而是要划到业务逻辑层中去如果不做分层的话那么让Useraspxcs来处理业务逻辑甚至操作数据库都没什么问题但是做分层的话这样就不应该了在分层结构中Useraspxcs仅应该处理与显示有关的内容其它部分都不应该涉及
举例我们实现用列表方式显示用户的功能那么提取信息的工作是由BLL来做的UI(本例中是Useraspxcs)调用BLL得到UserInfo后通过代码绑定到Useraspx的数据控件上就实现了列表的显示在此过程中Useraspxcs对UI没有起到什么作用仅是用来传递数据而且因为实际编码中大部分情况都是如此的实现所以使有些人觉得Useraspxcs不应该算UI而应该并入BLL负责逻辑处理继续往下看这时提出了一个新需求要求在每个用户的前面加一个图标生动地表现出用户的性别而且不满岁的用儿童图标表示这个需求的实现就轮到Useraspxcs来做了这种情况下Useraspxcs才算有了真正的用途
NewBLLcs 添加如下方法
public IList<UserInfo> GetUsers()返回所有的用户信息列表
public UserInfo GetUser(int UserId)返回指定用户的详细信息
public bool AddUser(UserInfo User)新增用户信息
public bool ChangeUser(UserInfo User)更新用户信息
public void RemoveUser(int UserId)移除用户信息
此文件就属于业务逻辑层了专门用来处理与业务逻辑有关的操作可能有很多人觉得这一层唯一的用途就是把表现层传过来的数据转发给数据层这种情况确实很多但这只能说明项目比较简单或者项目本身与业务的关系结合的不紧密(比如当前比较流行的MIS)所以造成业务层无事可做只起到了一个转发的作用但这不代表业务层可有可无随着项目的增大或者业务关系比较多业务层就会体现出它的作用来了
此处最可能造成错误的就是把数据操作代码划在了业务逻辑层而把数据库作为了数据访问层
举例有些朋友感觉BLL层意义不大只是将DAL的数据提上来就转发给了UI而未作任何处理看一下这个例子
BLL层
SelectUser(UserInfo userInfo)根据传入的username或email得到用户详细信息
IsExist(UserInfo userInfo)判断指定的username或email是否存在
然后DAL也相应提供方法共BLL调用
SelectUser(UserInfo userInfo)
IsExist(UserInfo userInfo)
这样BLL确实只起到了一个传递的作用
但如果这样做
BLLIsExist(Userinfo userinfo)
{
UerInfo user = DALSelectUser(User)
return (userInfoId != null)
}
那么DAL就无需实现IsExist()方法了BLL中也就有了逻辑处理的代码
UserModelcs 实体类这个东西大家可能觉得不好分层包括我以前在内是这样理解的UI?àModel?àBLL?àModel?àDAL如此则认为Model在各层之间起到了一个数据传输的桥梁作用不过在这里我们不是把事情想简单而是想复杂了
Model是什么?它什么也不是!它在三层架构中是可有可无的它其实就是面向对象编程中最基本的东西类一个桌子是一个类一条新闻也是一个类intstringdoublie等也是类它仅仅是一个类而已
这样Model在三层架构中的位置和intstring等变量的地位就一样了没有其它的目的仅用于数据的存储而已只不过它存储的是复杂的数据所以如果你的项目中对象都非常简单那么不用Model而直接传递多个参数也能做成三层架构
那为什么还要有Model呢它的好处是什么呢下面是思考一个问题时想到的插在这里
Model在各层参数传递时到底能起到做大的作用?
在各层间传递参数时可以这样
AddUser(userIduserNameuserPassword…)
也可以这样
AddUser(userInfo)
这两种方法那个好呢一目了然肯定是第二种要好很多
什么时候用普通变量类型(intstringguiddouble)在各层之间传递参数什么使用Model传递?下面几个方法
SelectUser(int UserId)
SelectUserByName(string username)
SelectUserByName(string usernamestring password)
SelectUserByEmail(string email)
SelectUserByEmail(string emailstring password)
可以概括为
SelectUser(userId)
SelectUser(user)
这里用user这个Model对象囊括了usernamepasswordemail这三个参数的四种组合模式UserId其实也可以合并到user中但项目中其它BLL都实现了带有id参数的接口所以这里也保留这一项
传入了userInfo那如何处理呢这个就需要按照先后的顺序了有具体代码决定
这里按这个顺序处理
首先看是否同时具有username和password然后看是否同时具有email和password然后看是否有username然后看是否有email依次处理
这样如果以后增加一个新内容会员卡(number)则无需更改接口只要在DAL的代码中增加对number的支持就行然后前台增加会员卡一项内容的表现与处理即可
UserDALcs public IList<UserInfo> SelectUsers()返回所有的用户信息列表
public UserInfo SelectUser(int UserId)返回指定用户的相信信息
public bool InsertUser(UserInfo User)新增用户信息
public bool UpdateUser(UserInfo User)更新用户信息
public void DeleteUser(int UserId)移除用户信息
很多人最闹不清的就是数据访问层到底那部分才算数据访问层呢?有些认为数据库就是数据访问层这是对定义没有搞清楚DAL是数据访问层而不是数据存储层因此数据库不可能是这一层的也有的把SQLHelper(或其同类作用的组件)作为数据访问层它又是一个可有可无的东西SQLHelper的作用是减少重复性编码提高编码效率因此如果我习惯在乎效率或使用一个非数据库的数据源时可以丢弃SQLHelper一个可以随意弃置的部分又怎么能成为三层架构中的一层呢
可以这样定义与数据源操作有关的代码就应该放在数据访问层中属于数据访问层
IUserDAL 数据访问层接口这又是一个可有可无的东西因为Petshop中带了它和ClassFactory类工厂所以有些项目不论需不需要支持多数据源都把这两个东西做了进来有的甚至不建ClassFactory而只建了IDAL然后IUserDAL iUserDal = new UserDAL()不知意义何在这就完全是画虎不成反类犬了
许多人在这里有一个误解那就是以为存在这样的关系BLL?àIDAL?àDAL认为IDAL起到了BLL和DAL之间的桥梁作用BLL是通过IDAL来调用DAL的但实际是即使你如此编码IUserDAL iUserDal = ClassFacotryCreateUserDAL()那么在执行iUserDalSelectUsers()时其实还是执行的UserDAL实例而不是IUserDAL实例所以IDAL在三层中的位置是与DAL平级的关系
通过上面的介绍基本上将三层架构的层次结构说明了其实本人有一个判断三层架构是否标准的方法那就是将三层中的任意一层完全替换都不会对其它两层造成影响这样的构造基本就符合三层标准了(虽然实现起来比较难^_^)例如如果将项目从B/S改为C/S(或相反)那么除了UI以外BLL与DAL都不用改动或者将SQLServer改为Oracle只需替换SQLServerDAL到OracleDAL无需其它操作等等本来想在文中加入一些具体的代码的但感觉不是很必要如果大家觉得需要的话我再补充吧
总结不要因为某个层对你来说没用或者实现起来特别简单就认为它没有必要或者摒弃它或者挪作它用只要进行了分层不管是几层每一层都要有明确的目的和功能实现而不要被实际过程所左右造成同一类文件位于不同层的情况发生也不要出现同一层实现了不同的功能的情况发生