Java EE设计模式(上)- 表现层

整体结构如下

表现层设计模式

在遗留代码中,尤其是存在着“坏味道”的代码中,开发人员必须先要理解和领会遗留代码中的行为和数据结构,新的代码也将以相同风格加入,不会有任何改进。

一个设计良好的软件,应该使用不同的配置参数来控制它的运行时行为,而不需要修改任何代码(对修改关闭,对扩展开放)。

主图如下:

1.前端控制器

控制器使用事件代码和屏幕代码的组合唯一标识每个用户动作以及后续即将渲染视图的处理代码。如果控制器的设计不当,则会导致执行效率低下,并产生过程式编程,后续增加的新特性也就不可避免地会增加 if-else 代码。

控制器会越来越大,并最终超出可控制范围,增加控制器并不能解决问题,而会导致越来越难用。基于面向对象组件的应用程序开发的一个最主要目标就是复用,过多的JSP/Servlet只会强调过程化能力而降低复用。

模式目的

  • 过多的控制器,会增加维护和复用的难度;
  • 整个应用程序只应该有一个入口点;
  • 控制器遵循SRP,应捕获请求、委派可插入组件来负责业务逻辑调用和视图选择;
  • JSP不应该被用作控制器;
  • 围绕单一入口点,声明式扩展功能;

解决方案

SpringMVC:DispatcherServlet,框架的基础,并与Spring IOC集成。

扫描二维码关注公众号,回复: 10953583 查看本文章

DispatcherServlet捕获客户端的所有Web请求,然后把它们转发到相应的页面控制器。前端控制器Servlet和页面控制器Controller会相互配合,协同工作,形成事件驱动Web应用程序的核心。

前端控制器中不包含任何if-else代码,将其都迁移至页面控制器,前端控制器是一个可在应用程序中复用的公共组件。

2.应用程序控制器

对于JSP控制器来说,负责处理下述与请求有关的任务:

  1. 捕获传入的请求;
  2. 调用业务组件;
  3. 标识和重定向到下一个视图;

前端控制器解决第一个问题,剩下两个交给前端控制器解决也没问题,但这会违背单一职责原则,会导致非常不灵活而且越发庞大。

采用分而治之的策略,从前端控制器中将“动作处理器”和“视图处理器”分离出来,轻量化前端控制器。

主要流程为:前端控制器首先要与动作处理程序交互,随后会调用页面控制器。

模式目的

  • 从前端控制器中消除动作管理和视图功能,使前端控制器尽量通用,并且尽量轻量级;
  • 支持不同类型的页面控制器和视图处理器,增强复用性、聚合度和模块化;

Spring框架解决方案

在DispatchServlet的帮助下,SpringMVC提供了类似的动作-视图管理支持,分解成两个不同部分会极大地提高其灵活性。

动作处理器

HandlerMapping:动作处理器的核心,在捕获请求时,分发者Servlet会查找合适的处理程序映射对象,处理程序映射。

SpringMVC中支持多个处理程序映射同时工作,但需要指定其优先级以确定其先后次序。

处理程序映射仅存储对页面控制器的引用而不会调用其任何方法(相当于注册表,提供注册/查找等功能)。而处理程序适配器都实现HandlerAdaptor(遵循适配器模式,GOF),并能够调用合适的页面控制器,换句话说,它知道如何调用handleRequest并处理返回值。

HandlerExecutionChain中包含了一组可选的拦截器,参考拦截器模式。

视图处理器

视图处理程序从动作处理器分离出来,这一功能是通过View和ViewResolver实现的。

View是所有可使用的表示层技术抽象(jsp,velocity,freemarker),视图可以显示业务对象调用的结果,也可以显示与用户交互的控件。

但从应用程序控制器和视图管理的角度来看,最重要的接口却是ViewResolver,负责完全拆分视图和控制器。

3.页面控制器

页面控制器仅仅操作一个逻辑页面,而前端控制器则操作一系列页面。当多个页面控制器拥有共同的逻辑时,一个常见的经验就是把共同逻辑移到BasePageController,页面控制器继承基类。

这是实现集中请求逻辑的方式。当BaseController开始对不同的请求有不同的处理逻辑时,这时考虑前端控制器是一个好主意。

页面控制器在Spring容器中注册,并实现GOF命令模式+模板方法模式。该模式的主要目的是:

  • 删除响应用户动作以调用业务逻辑的代码,以复用组件;
  • 基于请求URL而不是因编码和屏幕代码来标识可复用组件;
  • 为每个用户动作都部署一个可复用组件;

使用统一的页面控制器,来固化用户动作处理流程,以增强复用性、可扩展性,并支持生命周期。

4.上下文对象

以前的页面控制器,既需要自己处理bean,还需要自己处理请求,是典型的有状态服务。

抽象出上下文对象,来处理并将请求绑定到Context,进行无状态服务的调用。

使用上下文对象的主要目的,是可以在不依赖任何协议的情况下封装和共享表单数据。

  • 不要严重依赖特性协议的API,这种依赖关系会降低应用程序代码的复用性。
  • 确定适合使用特性协议代码的上下文,将其限制再前端控制器或者应用程序控制器。

表现层代码,要想能够尽可能地复用,就要解除HTTP协议和Servlet API之间的紧耦合。

对于页面控制器来说,都会有Binder对象来实现HTTP参数值与上下文对象之间的绑定,处理所有与特定协议相关的细节。

虽然使用上下文对象可以提高复用性,并支持各种客户端,易于系统的相关测试,但同样也会降低系统性能(尤其是使用反射)。

5.拦截过滤器

一些通用的操作,例如授权检查会在AOP中进行,因为不能在所有的控制器中都复制这些代码。如果将其放在通用组件中,并且使用声明式方式透明地应用控制器,不仅非常适用,还可以增强程序的灵活性。

  • 使用该模式可以把公共处理操作集中在可复用组件中;
  • 预处理和后处理组件应该与已有应用程序代码进行松耦合;
  • 声明性地应用公共处理;

Servlet过滤器

Servlet API内置的过滤器,可能有助于解决前面所提到的需求,所有Web容器都支持过滤器,当控制权传递到目标Servlet之前,或离开Servlet之后。

事实上,可以为每个请求配置过滤器链:

Spring拦截器

HandlerInterceptor,该接口中提供了一些具体实现和有用的抽象类,用以构建处理程序拦截器函数。

6.视图助手

作为调用业务逻辑的结果,页面控制器所返回的数据被视图组件用于提供最终的动态响应。

例如使用JSP技术时,业务组件返回的数据被设置为请求属性,需要使用内嵌的程序逻辑把动态数据与JSP的静态标记和模板文本整合到一起,脚本语言的使用极大地降低了复用性,增加了维护的难度。

该模式的主要目的:

  • 从基于模板的视图中删除编程逻辑;
  • 实现Java开发者和网页制作者之间的清晰分工;
  • 创建可复用组件,可以合并多个视图的模型数据;

注意:视图助手不应该负责调用业务或数据访问逻辑,将其功能仅限于显示用户数据方面。

标签库提供了通用的可复用组件,用于满足目前为止使用JavaBean助手或者脚本语言所处理的不同需求,此外,还可以使用高效的基于组件的第三方标签库开发灵活、稳健的视图组件。

  1. JSTL标签:Java标准标签库;
  2. Spring标签:基于组件的视图,还可以显示输入文本域和验证错误信息;
  3. 使用第三方标签库

7.组合视图模式

开发和维护视图组件是一件令人生畏的任务,不仅要求适配静态模板和动态数据,还需要用更小的可复用子视图来创建视图,这样可以提高视图的复用性,并且可以降低管理和维护的难度。

每个视图都由以下3个元素组成:

  • 组件:UI控件,如按钮、文本框等等;
  • 容器:组件的集合;
  • 布局:负责在容器中定位/调整不同组件的大小;

该模式的目的是:

  • 使用可复用的子视图用作标题、页脚、导航、菜单;
  • 标识和构造可复用的组件和容器;
  • 将组件和容器放在合适的布局中,以便可以灵活修改;

组合视图模式是两个众所周知GOF设计模式的组合体:组合和策略,布局提供了由小的组合子视图构成更大视图组件的策略。

Spring中提供与视图布局框架的集成,利于开发和维护组合视图:

  • SiteMesh: 非侵入性,基于Servlet过滤器,只需进行配置即可。
  • Apache Tiles:灵活的、扩展性强的布局框架。

同样,使用组合视图模式可以提高灵活性和复用性,但是以性能部分下降为条件。

8.分发者视图

在应用程序中,有不需要业务逻辑粗粒的大量静态视图,这些静态视图可以使用缓冲数据来支持半静态视图,基于上述考虑,可以使用分发者视图来处理静态或半静态视图。

分发者视图是综合其他表现层模式的最佳实践,它把分发者委托给视图,这种情况下,分发者是控制器和视图解析器的结合体。

在Spring框架中,例如 InternalResourceViewResolver,半静态视图已经以某种形式缓存所需的数据,为了使用这些数据可以采用视图助手。

这样就形成了最佳实践,为合并表现层模式确定了清晰的指导原则,且易于实现。

9.服务到工作者模式

使用服务到工作者模式,可以通过调用不同层的组件,即可调整请求处理流程。

与分发者视图一样,服务到工作者模式本质上是创建分层Java EE应用程序的规范。

服务到工作者模式为表现层和业务层的连接铺平了道路,两个层之间的桥接由业务代理模式提供,页面控制器模式为表现层和业务层的连接铺平了道路。

一般业务代理对象,通过Spring容器注入到控制器,这种方式:

  • 形成了最佳实践,设定了清晰的规则,提供了只可从页面控制器连接业务组件的指令;
  • 易于实现,并定义清晰的角色分工;

猜你喜欢

转载自www.cnblogs.com/zhiqiangma/p/12731868.html
今日推荐