分层架构概要设计架构设计基本原则这里,将描述一些在这个架构设计中的基本原则,其中很多都是经典的设计原则逐层调用原则及单向调用原则现在约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。那么,我们设计的架构应该满足以下两个原则:第K(1K=N)层只准依赖第K-1层,而不可依赖其他底层。如果P层依赖Q层,则P的编号一定大于Q。其中第一个原则,保证了依赖的逐层性,及整个架构的依赖是逐层向下的,而不能跨层依赖。第二个原则,则保证了依赖的单向性,及只能上层依赖底层,而不能底层反过来依赖上层。针对接口编程,而不是针对实现编程这里所指的接口,不是特指编程语言中的具体语言元素(如C#中由Interface定义的语言接口),而是指一种抽象的,在语义层面上起着接合作用语义体。它的具体实现,可能是接口,可能是抽象类,甚至可能是具体类。从不同的视角,接口可以有以下两种定义:1.接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。2.接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。具体到N层架构中,针对接口编程的意义在部分上是这样的:现仍约定将N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层,那么第K层不应该依赖具体一个K-1层,而应该依赖一个K-1层的接口,即在第K层中不应该有K-1层中的某个具体类。依赖倒置原则在软件设计原则中,有一种重要的思想叫做依赖倒置。它的核心思想是:不能让高层组件依赖底层组件,而且,不管高层组件和底层组件,两者都应依赖于抽象。那么,这个原则和我们上面的原则是否矛盾呢?其实并不矛盾。因为这个原则定义中的“依赖”是指“具体依赖”,而上面定义中的依赖全部指“抽象依赖”。我对这两种依赖的定义如下:具体依赖——如果P层中有一个或一个以上的地方实例化了Q层中某个具体类,则说P层具体依赖于Q层。抽象依赖——如果P层没有实例化Q层中的具体类,而是在一个或一个以上的地方实例化了Q层中某个接口,则说P层抽象依赖于Q层,也叫接口依赖于Q层。依赖倒置原则从这两个定义可以看到,所谓的依赖倒置原则,正是上面提到针对接口编程,而不是针对实现编程,两者在本质上是统一的。综上所述,可以看出,本课题设计的分层架构,应该是这样一种架构:1.N层架构的各层依次编号为1、2、…、K、…、N-1、N,其中层的编号越大,则越处在上层。2.架构中仅存在一种依赖,即第K层接口依赖第K-1层,其中1K=N。封装变化原则封装变化的原则定义为:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混杂在一起。开放-关闭原则开发-关闭原则定义为:对扩展开放,对修改关闭。具体到N层架构中,可以描述为:当某一层有了一个新的具体实现时,它应该可以在不修改其他层的情况下,与此新实现无缝连接,顺利交互。单一归属原则在这个架构中,任何一个操作类都应该有单一的职责,属于单独的一层,而不能同时担负两种职责或属于多个层次(实体类及辅助类可以被多个层使用,但它们不属于任何一个层,而是独立存在)。层次划分目前,典型的分层架构是三层架构,即自底向上依次是数据访问层、业务逻辑层和表示层。这种经典架构经历了时间的考验和实践的多次检验,被认为是合理、有效的分层设计,所以,在本文中,将沿袭这种经典架构,使用数据访问层、业务逻辑层和表示层的三层架构体系。职责划分数据访问层——负责与数据源的交互,即数据的插入、删除、修改以及从数据库中读出数据等操作。对数据的正确性和有效性不负责,对数据的用途不了解,不负担任何业务逻辑。业务逻辑层——负责系统领域业务的处理,负责逻辑性数据的生成、处理及转换。对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户性数据不负责,对数据的呈现样式不负责。表示层——负责接收用户的输入、将输出呈现给用户以及访问安全性验证。对流入的数据的正确性和有效性负责,对呈现样式负责,对流出的数据正确性不负责,但负责在数据不正确时给出相应的异常信息。模块划分及交互设计实体类模块:一组实体类的集合,负责整个系统中数据的封装及传递。数据访问层接口族:一组接口的集合,表示数据访问层的接口。业务逻辑层接口族:一组接口的集合,表示业务逻辑层的接口。数据访问层模块:一组类的集合,完成数据访问层的具体功能,实现数据访问层接口族。业务逻辑层模块:一组类的集合,完成业务逻辑层的具体功能,实现业务逻辑层接口族。表示层模块:程序及可视元素的集合,负责完成表示层的具体功能。IoC容器模块:负责依赖注入的实现。辅助类模块:完成全局辅助性功能。模块划分及交互设计各模块见交互关系如下实体类的设计与实现实体类是现实实体在计算机中的表示。它贯穿于整个架构,负担着在各层次及模块间传递数据的职责。一般来说,实体类可以分为“贫血实体类”和“充血实体类”,前者仅仅保存实体的属性,而后者还包含一些实体间的关系与逻辑。大多情况下,实体类和数据库中的表(这里指实体表,不包括表示多对多对应的关系表)是一一对应的,但这并不是一个限制,在复杂的数据库设计中,有可能出现一个实体类对应多个表,或者交叉对应的情况。系统的工程结构在初始阶段,整个系统包括6个工程,它们的职责是这样的:Web——表示层Entity——存放实体类Factory——存放和依赖注入及IoC相关的类IBLL——存放业务逻辑层接口族IDAL——存放数据访问层接口族Utility——存放各种工具类及辅助类这只是一个初期架构,主要是将整个系统搭一个框架,在后续开发中,将会有其他工程被陆陆续续添加进来。实体类将放在Entity工程下接口的设计与实现对于接口的设计。这里包括数据访问层接口和业务逻辑层接口。在分层架构中,接口扮演着非常重要的角色,它不但直接决定了各层中的各个操作类需要实现何种操作,而且它明确了各个层次的职责。接口也是系统实现依赖注入机制不可缺少的部分。接口设计将按如下顺序进行:1.首先由需求分析,列出主要的UI部分。2.分析各个UI需要什么业务逻辑支持,从而确定业务逻辑层接口。3.分析业务逻辑层接口需要何种数据访问操作,从而确定数据访问层接口。另外,为保证完全的面向对象特性,接口之间的数据传递主要靠实体类或实体类集合,禁止使用DataTable等对象传递数据。数据访问层的一种实现:Access+SQL通过前面的工作,整个系统的框架算是基本搭建完了,下面,我们要具体实现各个层次。关于数据访问层的实现,我们讨论三种实现方式,第一种:Access+动态生成SQL。顾名思义,这种实现将使用Access作为后台数据库,而操作方式也是最基本的使用SQL命令。在具体编写实现代码之前,我们需要做一些准备工作:第一步,我们要将Access数据库搭建完成,具体做法如下。在Web工程下新建一个文件夹,命名为AccessData,并在其中新建一个mdb文件(即Access数据库文件),按照需求分析的数据库设计构架,将数据表及表间关系建好,第二步,我们要进行一些配置。打开Web工程下的Web.config文件,在其中的appSettings节点下,添加如下键值:addkey=AccessConnectionStringvalue=Provider=Microsoft.Jet.OLEDB.4.0;DataSource={DBPath}/addkey=AccessPathvalue=~/AccessData/AccessDatabase.mdb/第一条为Access的连接字符串,第二条为Access数据库文件的路径,其中“~”表示网站根目录第三步,新建一个工程。我们要新建一个工程AccessDAL,用来存放Access数据访问层的代码。准备工作做完了,现在来实现具体的代码。1.编写数据访问助手类因为很多数据访问操作流程很相似,所以,这里将一些可复用的代码抽取出来,编写成助手类,以此减少代码量,提高代码复用性。这个助手类放在AccessDAL下,叫AccessDALHelper,主要负责Access数据库的访问。它包括三个方法:GetConnectionString:从配置文件中读取配置项,组合成连接字符串。ExecuteSQLNonQuery:执行指定SQL语句,不返回任何值,一般用于Insert,Delete,Update命令。ExecuteSQLDataReader:执行SQL语句返回查询结果,一般用于Select命令。2.实现具体的数据访问操作类因为前面已经定义了数据访问层接口,所以实现数据访问操作类就是很机械的工作了。这里主要包括三种类型的操作,一种是修改型,如Insert;一种是返回单个实体类型,如GetByID;还有一种是返回实体类集合型,如GetAll。依赖注入机制及IoC的设计与实现这里设计的分层架构,层与层之间应该是松散耦合的。因为是单向单一调用,所以,这里的“松散耦合”实际是指上层类不能具体依赖于下层类,而应该依赖于下层提供的一个接口。这样,上层类不能直接实例化下层中的类,而只持有接口,至于接口所指变量最终究竟是哪一个类,则由依赖注入机制决定。配置首先,需要在Web工程的Web.config文件的appSettings节点下添加如下两个项:addkey=DALvalue=/addkey=BLLvalue=/这两个配置选项分别存储要应用的数据访问和也业务逻辑层的程序集名称。value目前是空,是因为目前还没有各个层次的具体实现。实现缓存操作辅助类为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下封装依赖注入代码因为很多依赖注入代码非常相似,为了减少重复性代码,我们将可复用的代码先封装在一个类中。(这个类放在Factory工程下)实现工厂下面使用两个辅助类,实现数据访问层工厂和业务逻辑层工厂。业务逻辑层的实现在实际应用中,业务逻辑层是至关重要的,他承载着整个系统最核心的部分,也是客户最关注的部分。这一部分的实现,通常需要技术专家和领域专家通力合作。业务逻辑层主要承担了以下职责:1.对不同数据访问层的封装。使得表示层可以不关心具体的数据访问层。2.业务逻辑数据的填充与转换。如管理员口令的加密。3.核心业务的实现。这里很多业务逻辑只有一行代码,即一个业务逻辑方法恰好对应一个数据访问方法,但是也有通过多个数据访问方法实现业务的。如AdminBLL中的ChangePassword方法就调用了AdminDAL的GetByID和Update两个方法。另外,虽然许多方法只调用一个数据访问方法,但是从命名看也能看出两者着眼点的不同。如AdminDAL中的GetByNameAndPassword,这个名字显然是从数据库的角度看问题——指按照指定的Name和Password两个字段的值取出相应信息,至于这样做的业务意义它不需要知道。而AdminBLL中,调用它的方法叫Login,这是从业务角度看问题——即这个方法是管理员登录1.建立工程在这个架构中,业务逻辑层是可以替换的。及业务逻辑层不是直接耦合于表示层,而是通过依赖注入机制实现。所以,我们这里将这个业务逻辑层不直接命名为BLL,而是新建一个叫SimpleBLL的工程,放置我们这个业务逻辑层的相关代码。2.配置依赖注入业务逻辑层要通过反射工厂加载相应的数据访问层,这样就需要在Web.config中配置需要使用的数据访问层。打开Web.config,找到appSettings节点下的“DAL”项,将其中的value赋予我们要使用的数据访问层工程名称,例如:要使用NBearDAL,则这一项应该这样写:addkey=DALvalue=NBearDAL/3.编写散列加密工具类因为在业务逻辑层的多处需要用到散列加密,所以在Utility工程下写一个辅助类Encrypto