CategoryId构成主外键 分类名 图书出版社表Publishers 字段名 出版社名ID 字段 Id 类型长度 int 允许空 否 说明 主键 与Users表的PublisherId构成主外键 Name nvarchar(200) 否 出版社名称 用户表Users 字段名 用户ID Name nvarchar(200) 否 字段 Id 类型长度 int 允许空 否 说明 主键 与Orders表的UserId构成主外键 0表示普通会员 1表示管理员 1表示未审批 0表示正常 2表示冻结 登陆账号 登录密码 用户真实姓名 联系地址 联系电话 邮箱 用户角色 LoginId LoginPwd Name Address Phone Mail UserRoleId nvarchar(50) nvarchar(50) nvarchar(50) nvarchar(200) nvarchar(100) nvarchar(100) int 否 否 否 否 否 否 否 用户状态 UserStateId int 否 订单表(Orders)
字段名 订单ID 字段 Id 类型 长度 int 允许空 否 说明 主键 与OrderBook表的OerderId构成主外键 订购日期 用户ID 总额 订单状态 OrderDate UserId datetime int 否 否 否 否 Users表的外键 已发货 已收货已收款 TotalPrice decimal(10,2) OrderState nvarchar(10) 图书订单表(OrderBook) 字段名 图书订单ID 订单ID 图书ID 订购数量 单价
3.2 实体类设计
3.2.1 实体类概述、作用及设计目标
实体类是对实体的封装,是现实世界中实体的计算机表示。它通常包括私有
字段 Id OrderId BookId Quantity UnitPrice 类型 长度 int int int int Decimal(18,0) 允许空 否 否 否 否 否 说明 主键 Orders表的外键 Books表的外键 变量及对应的get、set方法,而在C#语言中,将get、set方法又组合成属性。这些私有变量或者属性,则对应现实实体相应的属性。
实体类的作用主要有两个,一是作为现实实体的计算机代表,二是数据的传
递。在分层架构的应用中,数据正是封装在实体类中,然后以实体类为载体在各个层次间传递。这样不但符合面向对象设计的原则,也便于对数据存取进行控制。
理想情况下,实体类中不能含有任何逻辑,它应该单纯是数据的封装。所以,
它不应该有方法,当然构造函数除外。
基于以上分析,我们设计的实体类,应该是准确、干净、易用。准确表明实
体类应该准确无误地表示现实中的实体,干净表示实体类应该仅包含数据的封装而不掺杂任何逻辑或者与数据封装无关的东西,易用表示实体类应该很容易地用
来在各层之间传递数据。
3.2.2 实体类的设计方案及其比较
一般认为,实体类的设计非常简单,而且系统中仅需要一种实体类即可。但
是,在分层架构中出现了一些新情况,导致了一些变化。
我们知道,实体类负责整个系统数据的传输,从表示层到数据访问层,甚至在JavaScript中,都能看到它的身影,因此可以说,它与各层的耦合度是相当高的,由于实体类的存在,系统各层之间多了一个间接的耦合,我把它叫做实体类耦合。实体类耦合有时是非常危险的。
理想的分层架构,应该是各层可独立替换的。例如,将数据访问层替换掉,
并不应该导致业务逻辑层和表示层的丝毫改动,只要实现了约定接口的数据访问层,都应该可以替换进来。但是,如果某一个新的数据访问层使用的实体类和原实体类不一致,那么当这个数据访问层替换进来后,就需要修改其它所有层次,将其中的实体类进行替换。这严重破坏了开放-关闭原则,也严重影响了分层架构的质量。
这种情况,在现实中是完全可能发生的。例如,我们之前是用的朴素数据访
问层设计,即动态生成SQL语言或调用存储过程的方法,那么,我们的实体类应该是已经设计好的“干净”实体类。而某一天,我们需要改用原有框架设计数据访问层,而假设原有机制用到的实体类是自动生成的,它是不“干净”的,其中添加了很多专为原有机制而设计的代码。这些实体类,与原实体类完全不兼容。
为避免这种情况,就需要对实体类耦合解耦。解耦的方法有两种:一是使用
Adapter模式,二是使用转换器。不过,本文并不会对这两种方法做详细的介绍。 3.2.3 实体类的实现
3.3 接口设计
3.3.1 接口概述及其作用
这里的“接口”一词,特指在分层架构中底层向顶层开放的可调用方法,具本课题约定,实体类命名规则为“表名”。 以用户实体为例,Users的完整代码见附录一。
体到本课题的Demo中,特指数据访问层接口和业务逻辑层接口。
接口在技术上编写难度不大,但是其意义十分重大。总体来说,接口有着一
下几个作用:
", 接口明确了各层次的职责。
", 接口决定了各个层次具体需要实现的功能。 ", 接口形成了整个分层架构的骨架
", 接口暴露了层次的API,为上层提供了依赖点。
因此,接口的设计实际上处在现实需求和程序实现之间,起到承上启下的用。
它决定了需求分析中的各个需求如何合理地映射成各个层次的不同方法。所以接口的设计应该在需求分析的基础上进行。 3.3.2 数据访问层接口的设计
因为接口直接关系到层次的职责,所以,在设计数据访问层接口之前,需要
对数据访问层的职责进行明确。
在本课题中,将数据访问层职责叙述如下:数据访问层负责与数据源的交互,
负责数据的创建、删除、更新及查询工作。它不应该包含任何业务逻辑或可视性元素,对它所处理数据的业务意义是“无知”的。它与数据库系统一起负责数据完整性。
具体来说,数据访问层的接口一般包含以下几种类型的操作:
创建:在数据库中插入新记录,无返回值或返回表示操作状态的标志值。 删除:在数据库中删除符合条件的记录,一般无返回值或返回表示操作状态
的标志值。
更新:将数据库中符合条件的记录更新,一般无返回值或返回表示操作状态
的标志值。
单实体查询:从数据库中读出符合条件单条记录的信息,一般返回单个实体
类。
集合实体查询:从数据库中读出符合条件的多条记录的信息,一般返回实体
类集合。
函数查询:根据一定的函数规则,根据数据记录查询相应的函数值,如查询
某个表的记录数目,返回指定函数值。
以实体用户为例,根据需求分析,数据访问层接口设计如表所示。
需求 获取用户信息 获取全部用户 获取特定状态用户 添加新用户 删除用户 更新用户信息 修改用户密码 修改用户状态 判断是否存在此用户 接口 GetModel GetModelList GetModelList AddUser DeleteUser UpdateUser UpdatePwd 参数 用户名 无 用户状态 返回值 用户实体类 用户实体类 用户实体类 操作类型 单实体查询 集合实体查询 集合实体查询 创建 删除 更新 更新 更新 函数查询 用户实体类 表示是否成功的布尔值 表示是否成功用户ID 的布尔值 用户实体类 表示是否成功的布尔值 用户实体类 表示是否成功的布尔值 UpdateUserState 用户状态ID、表示是否成功的布尔值 用户ID IsExist 用户名
表示是否成功的布尔值 具体IUsersDAL的实现代码请参考附录一
3.3.3 业务逻辑层接口的设计
与数据访问层接口的设计一样,在设计业务逻辑层的接口前,首先应明确其
职责。
本课题将业务逻辑层的职责叙述如下:业务逻辑层负责完成与系统领域相关
的业务逻辑操作,实现过程中的数据访问操作通过调用数据访问层实现。它对业务相关的数据有效性负责,但是不负责UI输入数据的有效性。业务逻辑层中不能含有与显示相关的逻辑,不能决定或影响数据最终的呈现样式。
由于不同领域的业务逻辑差别很大,所以无法像数据访问层那样对接口操作
做出明确的分类。
在实际项目开发中,业务逻辑层接口的设计往往要和领域专家合作。而在本
课题的Demo中,由于网上购物系统的领域业务大家都很熟悉,所以不用进行专门的领域逻辑调研。