Web分层设计研究
表现层设计(一)
Web的分层设计得益于设计模式的广泛应用。对每个希望在web中采用体系结构开发的系统,在考虑采用何种架构技术时,往往会将该架构的分层设计是否合理,是否职责分明放在考虑的首位。在web中,众所周知的MVC是目前多数开源web框架采纳的设计模式。但由于该模式的定义并不是非常的明确。各层所负的职责会有交叉的现象。我们在使用MVC,特别是在使用面向对象的方法设计系统结构时,可能出于降低耦合和提高内聚的考虑,会对这3层进行进一步细分。
我主张在系统设计中,概念完整性应该是最重要的考虑因素。也就是说为了反映一系列连贯的设计思路,宁可省略一些不规则的特性和改进,也不提倡独立和无法整合的系统,哪怕它们其实包含着许多很好的设计。
------《人月神话》
我们对一个功能完整的web系统进行分层,同样要对层次的概念一致性和层次功能的完整性进行严格的定义。
这里我把与客户端有直接交互关系的层次称为表现层,要注意的是MVC中的view的职责只负责页面数据显示,不进行数据的处理,但在实际开发中,对使用动态页面技术的web框架就很难单独做出一个纯的view层(除非你就使用静态页显示)。表现层大体如图所示:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/lemonfamily/3cbcc0c379384fb987bf8f40361c0c3b.png)
在遵循servlet协议的web框架中,来自客户端的请求和服务端的响应都需要经过web.xml的过滤。任何集成了表现层的web框架也都需要在web.xml中注册。一般的做法都是将所有特定的请求通过servlet mapping 或filter mapping 把控制权移交给一个基础转发类,实现对web.xml的托管。这个基础类是框架中实现表现层的核心。通过它,我们可以自立门户,实现导航控制和请求的过滤和分发。声明与控制层的交互方式等等。该基础类应该是可以配置的,否则如果使用硬代码修改基础类将会带来一系列的麻烦。最主要的问题来自于维护。其次是可移植性。通常的做法是为基础类订制一个格式严谨的功能完善的外部配置文件。而最适合的就是xml文件。xml的数据一致性、格式规范性和灵活性为我们定制丰富的表现层组件提供了良好的数据接口。
表现层的职责,应该只负责对客户端的请求数据进行导航,将上层数据响应给客户端。
最多再对客户端数据进行封装或为上层数据进行格式化(统一样式)输出。但这一点,很可能模糊了与控制层的界限。封装客户端数据交给控制层时,我们可以通过接口形式调用控制层的服务。这没什么问题,但对于如何回显上层数据,却会牵扯到是否关联到了控制层的职责的问题。在不同的业务需求下,我们通常需要对后台处理的数据进行再加工才能响应给客户端。简单的例子:对时间进行格式化输出。这大家可能都认为应该属于表现层的职责,或许不能说明什么。再看一个比较复杂的逻辑:一份表单,有3种状态:待确认、生效、作废。客户要求根据不同的角色权限和状态显示不同的编辑状态。比如销售人员,在查看一张待确认的表单时,应该显示“确认”、“修改”、“返回”按钮,而如果是上级管理人员查看该表单,则应该只显示“返回”按钮。其他状态的表单类似。这种情况,有一部分属于表现层的数据输出的职责,也有一部分属于控制层的逻辑(数据组装)。我们应该把该操作放在控制层还是表现层来做?
为了尽量的达到低耦合的目的,有人提出了中间层的概念。中间层主要负责对上层数据的进一步封装(来自客户端的数据封装相对容易实现数据格式统一,可以不由中间层负责)。然后把处理后的数据回显给客户端,这样中间层其实是表现层的一个子层,简单表示如下:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/lemonfamily/844db05077514fb28b7e2001299fc246.png)
虽然这样的做法并没有消除职责的耦合,但已经可以把耦合所带来的影响缩小到一个较小的中间层中了。
我们继续看第一个图。在对客户端的请求数据进行封装时,通常的做法是:如果请求的数据是个表单,则将请求的数据封装成一个javabean,该javabean 可以是简单的只有get、set方法的javabean,也可以是继承了某些特定功能的扩展的javabean。无论哪一种,实现起来都很简单,request的getparamentsmap方法就可以直接将表单数据映射成一个map,map事实上就是一个符合javabeans规范的对象,如果要转换成我们所需的普通的javabean,beanutil或propertyutil就可以帮我们忙了,前提是,客户表单对应项的名称和javabean对应的名称一致。
数据有了,我们就要寻找服务了(钱有了,要到哪家商店买东西?),这里的服务,也就是指调用哪个方法。为了能使系统识别并为我们匹配到对应的调用方法,必须给予系统一个服务名标识,这里可大有作为。不合理的设计会带来类爆炸或类膨胀的问题。而往往这方面,不少web框架实现起来都不是很优雅。归根结底在于大部分的表示层都是基于动作的,对于每个动作,即使操作同一个对象,系统也很难判断是否是操作了同一个对象,除非每个动作都有一个唯一的相同的标识。面向对象编程里,类是一个对象,我们可以在里面添加操作该对象的各种属性和方法供外部调用,外部调用它并不需要提供自己的身份(好比我们买商品不需要出示身份证一样),但在web开发中,让不同的动作操作同一个类确实存在一个身份辨别的问题。客观的说,基于组件模型的jsf在这方面做得很优秀,前端提前的事件绑定使得系统可以通过注册在组件上的backingbean直接找到对应的服务。不需要由客户端提供任何标识,这样我们在修改客户端功能时,免除了配置文件或标识的变更所带来的大量修改。而仅仅需要去考虑如何在backingbean中实现业务逻辑。
转自:http://blog.csdn.net/lemonfamily/article/details/1679681