引言:本文不是从理论的角度来探讨三层架构,而是用一个示例来介绍如何建设一个三层架构的项目,并说明项目中各个文件所处的层次与作用。写本文的目的,不是为了说明自己的这个方法有多对,别人的肯定不对,而是希望给那些初学三层架构却不知从何入手的朋友提供一点帮助。因为网上的文章,大多是注重理论的介绍,而忽略了具体的实践应用,或者有示例但讲得不透彻。导致看了之后,理论上又学习了一遍,但还是不知道代码怎么写。所以想从这个方面入手写一下,让从来没做过三层架构的初学者也能照猫画虎,写出代码来。文章表述的是笔者个人对三层架构的认识,肯定有许多不足的地方,欢迎大家指正,小弟也会根据反馈来修改这篇文章。文中的代码是伪代码,仅用来阐明思路。
    正文:
    一提三层架构,大家都知道是表现层( UI ),业务逻辑层( BLL )和数据访问层( DAL ),而且每层如何细分也都有很多的方法。但具体代码怎么写,到底那些文件算在哪一层,却是模模糊糊的。下面用一个简单的例子来带领大家实战三层架构的项目,这个例子只有一个功能,就是用户的简单管理。
    首先建立一个空白解决方案,添加如下项目及文件
    1 、添加 ASP.NET Web Application 项目,命名为 UI ,新建 Web Form 类型文件 User.aspx (含 User.aspx.cs )
    2 、添加 ClassLibrary 项目,命名为 BLL ,新建 Class 类型文件 UserBLL.cs
    3 、添加 ClassLibrary 项目,命名为 DAL ,新建 Class 类型文件 UserDAL.cs 。添加 SQLHelper 引用。(这个是微软的数据访问类,也可以不用,直接编写所有的数据访问代码。我一般用自己写的数据访问类 DataAccessHelper )。
    4 、添加 ClassLibrary 项目,命名为 Model ,新建 Class 类型文件 UserModel.cs
    5 、添加 ClassLibrary 项目,命名为 IDAL ,新建 Interface 类型文件 IUserDAL.cs
    6 、添加 ClassLibrary 项目,命名为 ClassFactory
    相信大家已经看出来了,这个和 Petshop 的示例没什么区别,而且更简单,因为在下也是通过 Petshop 学习三层架构的。但一些朋友对于这几个项目所处的层次,以及它们之间的关系,可能比较模糊,这里逐个说明一下:
    1 User.aspx User.aspx.cs     这两个文件(以及文件所属的项目,下面也是如此,不再重复强调了)都属于表现层部分。 User.aspx 比较好理解,因为它就是显示页面了。 User.aspx.cs 有些人觉得不应该算,而是要划到业务逻辑层中去。如果不做分层的话,那么让 User.aspx.cs 来处理业务逻辑,甚至操作数据库都没什么问题,但是做分层的话,这样就不应该了。在分层结构中, User.aspx.cs 仅应该处理与显示有关的内容,其它部分都不应该涉及。
    举例:我们实现用列表方式显示用户的功能,那么提取信息的工作是由 BLL 来做的, UI (本例中是 User.aspx.cs )调用 BLL 得到 UserInfo 后,通过代码绑定到 User.aspx 的数据控件上,就实现了列表的显示。在此过程中 User.aspx.cs 对 UI 没有起到什么作用,仅是用来传递数据,而且因为实际编码中大部分情况都是如此的实现,所以使有些人觉得 User.aspx.cs 不应该算 UI ,而应该并入 BLL 负责逻辑处理。继续往下看,这时提出了一个新需求,要求在每个用户的前面加一个图标,生动地表现出用户的性别,而且不满 18 岁的用儿童图标表示。这个需求的实现,就轮到 User.aspx.cs 来做了,这种情况下 User.aspx.cs 才算有了真正的用途。
    2 NewBLL.cs     添加如下方法:
  

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 确实只起到了一个传递的作用。
    但如果这样做:

BLL.IsExist 
 ( 
 Userinfo userinfo 
 )
 
     {
 
      
 ( 
 User 
 );
         
 return (userInfo.Id != null);  
 
    }


    那么 DAL 就无需实现 IsExist() 方法了, BLL 中也就有了逻辑处理的代码。
    3 UserModel.cs     实体类,这个东西,大家可能觉得不好分层。包括我以前在内,是这样理解的: UI ß à Model ß à BLL ß à Model ß à DAL ,如此则认为 Model 在各层之间起到了一个数据传输的桥梁作用。不过在这里,我们不是把事情想简单,而是想复杂了。
    Model 是什么?它什么也不是!它在三层架构中是可有可无的。它其实就是面向对象编程中最基本的东西:类。一个桌子是一个类,一条新闻也是一个类, int 、 string 、 doublie 等也是类,它仅仅是一个类而已。
    这样, Model 在三层架构中的位置,和 int , string 等变量的地位就一样了,没有其它的目的,仅用于数据的存储而已,只不过它存储的是复杂的数据。所以如果你的项目中对象都非常简单,那么不用 Model 而直接传递多个参数也能做成三层架构。
    那为什么还要有 Model 呢,它的好处是什么呢。下面是思考一个问题时想到的,插在这里:     
    Model 在各层参数传递时到底能起到做大的作用?
    在各层间传递参数时,可以这样:
    AddUser ( userId , userName , userPassword , … ,)
    也可以这样:
    AddUser ( userInfo )
    这两种方法那个好呢。一目了然,肯定是第二种要好很多。
    什么时候用普通变量类型( int,string,guid,double )在各层之间传递参数,什么使用 Model 传递?下面几个方法:

SelectUser 
 ( 
 int UserId 
 )
 
     SelectUserByName 
 ( 
 string username 
 )
 
     SelectUserByName 
 ( 
 string username 
 , 
 string password 
 )
 
     SelectUserByEmail 
 ( 
 string email 
 )
     
 SelectUserByEmail 
 ( 
 string email 
 , 
 string password


    可以概括为:
    SelectUser ( userId )
    SelectUser ( user )
    这里用 user 这个 Model 对象囊括了 username , password , email 这三个参数的四种组合模式。 UserId 其实也可以合并到 user 中,但项目中其它 BLL 都实现了带有 id 参数的接口,所以这里也保留这一项。 
    传入了 userInfo ,那如何处理呢,这个就需要按照先后的顺序了,有具体代码决定。
    这里按这个顺序处理
    首先看是否同时具有 username 和 password ,然后看是否同时具有 email 和 password ,然后看是否有 username ,然后看是否有 email 。依次处理。
    这样,如果以后增加一个新内容,会员卡( number ),则无需更改接口,只要在 DAL 的代码中增加对 number 的支持就行,然后前台增加会员卡一项内容的表现与处理即可。 
    4 UserDAL.cs     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 ,一个可以随意弃置的部分,又怎么能成为三层架构中的一层呢。
    可以这样定义:与数据源操作有关的代码,就应该放在数据访问层中,属于数据访问层
    5 IUserDAL     数据访问层接口,这又是一个可有可无的东西,因为 Petshop 中带了它和 ClassFactory 类工厂,所以有些项目不论需不需要支持多数据源,都把这两个东西做了进来,有的甚至不建 ClassFactory 而只建了 IDAL ,然后“ IUserDAL iUserDal = new UserDAL(); ”,不知意义何在。这就完全是画虎不成反类犬了。
    许多人在这里有一个误解,那就是以为存在这样的关系: BLL ß à IDAL ß à DAL ,认为 IDAL 起到了 BLL 和 DAL 之间的桥梁作用, BLL 是通过 IDAL 来调用 DAL 的。但实际是即使你如此编码:“ IUserDAL iUserDal = ClassFacotry.CreateUserDAL() ;”,那么在执行“ iUserDal.SelectUsers() ”时,其实还是执行的 UserDAL 实例,而不是 IUserDAL 实例,所以 IDAL 在三层中的位置是与 DAL 平级的关系。
    通过上面的介绍,基本上将三层架构的层次结构说明了。其实,本人有一个判断三层架构是否标准的方法,那就是将三层中的任意一层完全替换,都不会对其它两层造成影响,这样的构造基本就符合三层标准了(虽然实现起来比较难 ^_^ )。例如如果将项目从 B/S 改为 C/S (或相反),那么除了 UI 以外, BLL 与 DAL 都不用改动;或者将 SQLServer 改为 Oracle ,只需替换 SQLServerDAL 到 OracleDAL ,无需其它操作等等。本来想在文中加入一些具体的代码的,但感觉不是很必要,如果大家觉得需要的话,我再补充吧。
    总结:不要因为某个层对你来说没用,或者实现起来特别简单,就认为它没有必要,或者摒弃它,或者挪作它用。只要进行了分层,不管是几层,每一层都要有明确的目的和功能实现,而不要被实际过程所左右,造成同一类文件位于不同层的情况发生。也不要出现同一层实现了不同的功能的情况发生。