跟着项目学设计模式(六):三层架构
前面用5个章节介绍了单例模式和工厂系列模式,这个过程中,如果算上网站开发人员的表示层,那么项目经历了二层到多层的演变:
数据访问层+表示层 =》
数据访问层+业务逻辑层+Client层+表示层
至此,这个系列中对创建型设计模式的介绍也到此为止了。后面的文章,我们会把关注点放到具体的代码实现上,比如层与层之间如何调用,工厂模式与抽象工厂在代码实现上的异同等等,这里面会涉及到很多结构型设计模式相关的知识,由于代码架构变得愈加复杂,也只有请结构型设计模式来救场。
那有人要问了,既然提升了系统复杂度,为什么要使用多层架构?
什么是三层架构?
为什么要用三层架构?
为了达到“高内聚,低耦合”的程序设计思想,引入了三层架构,在表现层和业务层分离开来,再加上数据访问层,便形成了今天的三层架构。
没错上面又是百度百科的解释,虽然这个答案简练得连面试的要求都达不到,但无疑是百分百正确的答案,没有掺杂任何个人理解的回答。初学者最不喜欢的就是这种答案,他们更喜欢的是富有个人强烈感情色彩的解释,即使偏激。
来看掺杂了笔者个人理解,详细到能应付面试的解答:
要回答这个问题首先要知道什么是一层架构和二层架构
一层架构:只有一个表示层
二层架构: 第一层:表示层
第二层:数据访问层
没错,一层架构就是只有一个表现层的架构。您可能会有疑问:只有一个表示层,怎么可能?
这里以后OA系统的用户模块为例,给一个5-10人的小公司用,只需要一个管理员账户,用户名密码都是admin,所有人都用这一个账号登录,代码里写死就可以了,业务逻辑就是登陆的时候判断 if(username=="admin"&&pwd=="admin")。
是的,你没有任何理由来批判一层架构的缺点,因为架构本身并没有缺点,只有适不适合。只是一个显示"Hello World"的功能,是不需要分什么层的,当然,如果非要有云"Hello World"这种需求,那就不好说了。
后来,使用OA的公司慢慢做大,成了几百人的公司,OA系统需要增加考勤模块作为主打功能,这意味着每个员工都需要一个用户名来做考勤,而admin则负责管理用户。我们需要在数据库添加用户表和门禁表。
管理员可以通过表单编辑用户表中的性别、出生日期和照片,用户只能编辑部分个人信息(照片和个性签名)
管理员修改用户信息
[Authorize("admin")]
[HttpPost]
public ActionResult AdminEdit(User entity)
{
using(var context=new DBContext())
{
var user = context.Users.Where(d => d.id == entity.id).FirstOrDefault();
user.sex=entity.sex;
user.birth=entity.birth;
user.photo=entity.photo;
if(context.SaveChanges()>0)
{
...
}
....
}
}
用户修改个人信息,我们把上面的代码copy过来,略加修改就可以了。
[Authorize]
[HttpPost]
public ActionResult UserEdit(User entity)
{
using(var context=new DBContext())
{
var user = context.Users.Where(d => d.id == entity.id).FirstOrDefault();
user.photo=entity.photo;
user.sign=entity.sign;
if(context.SaveChanges()>0)
{
...
}
....
}
}
表示层中多个用户表的编辑操作,造成了代码的重复,重复是远比坏味道还要严重的多的问题,我们修要提供一个通用的修改用户信息的方法来优化代码:
private bool UpdateUser(User entity,params string[] columnNames)
{
...
do Update
...
}
[Authorize("admin")]
[HttpPost]
public ActionResult AdminEdit(User entity)
{
//如果编辑成功
if(UpdateUser(entity, "sex", "birth","photo"))
{
...
}
}
[Authorize]
[HttpPost]
public ActionResult UserEdit(User entity)
{
//如果编辑成功
if(UpdateUser(entity, "photo","sign"))
{
...
}
}
一层架构为解决数据库操作造成的代码重复,不断地提供的通用方法,他们分散在表示层中的各个角落,这便是数据访问层的原型。
二层架构将这些通用方法抽离成数据访问层:
[Authorize("admin")]
[HttpPost]
public ActionResult AdminEdit(User entity)
{
//如果编辑成功
if(new UserDal().Update(entity, t => t.id == entity.id, "sex", "birth","photo"))
{
...
}
}
[Authorize]
[HttpPost]
public ActionResult UserEdit(User entity)
{
//如果编辑成功
if(new UserDal().Update(entity, t => t.id == entity.id, "photo","sign"))
{
...
}
}
所以,对于高质量的代码下的一层架构来说,重构成二层所带来的改变仅仅是把对数据库的通用方法拿出来,换个地方而已,并没有其他任何复杂的改动。
{{
【能够通过sonar代码质量检查的一层架构】 VS 【能够通过sonar代码质量检查的二层架构】
优点:代码好找(管理)了,以前是在本模块里找,现在是在本模块的某一层里面找,利于分工,使开发人员可以专注于某一层。
缺点:维护起来变麻烦了,每次改动需要提交的文件变多了
具体要不要重构,看项目实际情况和个人喜好吧
----------------------------------------------------------------------------------------------------
【能够通过sonar代码质量检查的一层架构】 VS 二层架构
毫无疑问,二层架构会被完爆!!!为什么?因为只要是能够通过sonar代码质量检查的一层架构重构的二层架构,只是把代码换了个位置,略加修改,肯定通过sonar代码质量检查的。
所以【能够通过sonar代码质量检查的一层架构】就约等于【能够通过sonar代码质量检查的二层架构】
如果连一层架构的代码都组织不好,何谈二层?
}}
-----------------------------------------------------------------------------------------------------------------------
如果后面项目需求变得更加复杂,业务规则、合法性校验等业务逻辑增多,高质量代码下的一层架构肯定是提供了很多业务逻辑的通用方法,因此
高质量代码下的一层架构可以随意的重构到二层和三层,物理文件的增加,代码位置的变动,仅此而已。
所以呢,人们都说高手所见略同,模块到了他们那群高手的手中,虽然在物理上并没有分层,但是分层的思想已经随处可见了。独孤求败草木竹石皆可为剑,就是这个道理。
但是正如三国演义里,吕布说过:“我有赤兔马方天戟,过河如履平地!”,这个时候陈宫说:“你有赤兔马,你当世无敌,但是将士们不行呀!”
你不能要求所有人都有那么高的水平,笔者去年参加的项目,也是多层架构,要求是代码合格率达到60%就可以了,当时写代码就跟应付考试似的,实在是能力有限,但是进度赶得紧。
要记住,是因为通用方法的出现才不得不在物理上分层,而不是分层之后,才去写通用方法。如果一开始你分层的目的是妄图通过分层达到变相的约束程序员去写通用方法的做法,是很难实现的。
真正起到一些作用的是接口,是设计模式。我接口里声明了这些方法,你就必须去实现,而且要调用这些通用方法也只能通过接口,而设计模式来决定接口到底如何设计,我单层架构也可以用抽象工厂模式,你管我放哪儿干嘛?我就是把所有的接口和类全写一个文件里它也是抽象工厂模式。
面试问题:
什么是三层架构:
从三层架构的视角来看一层和二层:
一层架构:包含复杂数据库操作和业务逻辑的表示层
二层架构: 第一层:包含复杂业务逻辑的表示层
第二层:数据访问层
为了达到“高内聚,低耦合”的程序设计思想,引入了三层架构,将表现层和业务层分离开来,把业务规则、数据访问、合法性校验等通用方法都放到了中间层进行处理,再加上数据访问层,便形成了三层架构。
三层架构的优缺点:
【能够通过sonar代码质量检查的一(二)层架构】 VS 【能够通过sonar代码质量检查的三层架构】
【能够通过sonar代码质量检查的二层架构】 约等于【能够通过sonar代码质量检查的三层架构】只是通用方法位置不同。
优点:代码好找(管理)了,以前是在本模块里找,现在是在本模块下的某一层里面找,利于分工,使开发人员专注于某一层。
缺点:维护起来变麻烦了,每次改动需要提交的文件变多了
【能够通过sonar代码质量检查的一(二)层架构】 VS 【三层架构】
没有可比性,架构本身并不能解决代码质量的问题。
真正起到一些作用的是接口,是设计模式。设计模式通过接口约束必须实现哪些方法,层与层之间只能调用通过接口中声明的方法。这在某种程度上是一种限制,但并不影响我在具体的实现该方法的代码里,究竟干了什么。
上一篇: 反射机制解析
下一篇: 设计模式(2):工厂模式