http://developer.java.sun.com/developer/technicalArticles/J2EE/J2EEevolution/
by Dan Malks和Deepak Alur
June 2003
在2001年的JavaOne大会上,我们推出了《J2EE核心模式》,里面包含一个模式目录,该模式目录中涉及了15个J2EE的设计模式。这些模式是J2EE平台下软件设计师和系统架构师经常遇到的典型问题及其解决方案。这本书里面也列举了无数糟糕的实现并对它们进行了重构,通过这些例子的学习,你可以改善你现有的设计。所有的这些模式都是在我们给客户创建成功的J2EE应用过程中被发现并提炼出来的。
模式与交流技术问题及其解决方案息息相关,他们可以让我们记录一些反复出现的问题及其解决方案并与其他人进行交流。写《J2EE核心模式》的一个动力就是人们常把“学习J2EE技术”与“学习用J2EE技术进行设计”混淆起来。现存的许多Java书籍大都介绍J2EE技术的各个方面,却几乎没有哪本书介绍如何应用这些技术。基于作为Sun专家服务Java中心的Java系统架构师的经验,我们觉得我们可以填补这个空白。
不过那已经是过去了,下面我们谈现在
《J2EE核心模式》第一版出版以后,我们收到读者对那15个模式大量的反馈意见。过去的几年中,J2EE模式讨论区非常活跃,注册会员也超过2000人。我们很高兴能从
[email protected]收到大量读者的直接反馈意见。这几年中,我们又做了几个重要的大型J2EE结构,也参与几个项目的开发,其间我们不断地应用这些模式,同时又总结出了另外的一些模式。
基于大家的反馈以及我们在工作中取得的经验,在2003年的JavaOne大会上,我们又推出该书的第二版。原有的15个模式全部都进行了修正和更新,引进许多新的实现策略和例程,增加了有关Web服务(Web service)的内容,覆盖了最新的J2EE1.4。我们又引进了6个新的模式,那么现在一共有21个J2EE设计模式。新增加的6个模式主旨在于改进模式语言,提供了高度抽象,你可以用来构造和理解J2EE应用。
模式目录
我们在第二版种引入了6个新的模式,每一层两个:
表示层模式:
环境对象(Context Object)
应用控制器(Application Controller )
业务层:
应用服务(Application Service )
业务对象(Business Object )
集成层模式:
域存储(Domain Store)
Web服务代理(Web Service Broker )
下面图中显示了模式目录中的21个模式,图1、图2、图3分别显示了各层中的模式,图4显示了各个模式之间的关系。请注意,所有的模式都已经修正并更新过,包括了一些新的实现策略以及一些新的例程。
图1: 表示层模式
图2: 业务层模式
图3: 集成层模式
图4: 各模式之间的关系
精选模式
这一部分,我们对新引进的6个模式的一小部分进行一个简单的介绍。其他更详细的信息,包括其他模式、实现策略、例程、UML框图、糟糕的实现及J2EE重构等,请参考"J2EE核心模式"。
虽然直到现在环境对象(Context Object)才被记录为一个模式,但是实际上我们在工作中会经常用到它。环境对象(Context Object)的使用可以提高系统的的可复用性、可维护性、可扩展性以及易测试性。下面我们介绍一下这个模式所涉及的问题、解决方案、UML类图、UML序列图以及使用这个模式的后果。
环境对象(Context Object)
问题
你想避免在相关环境之外使用与系统特定协议有关的信息。
一个应用程序总是要通过请求/响应的生命周期使用一些系统信息,比如请求信息、配置信息以及安全性数据等,这些系统信息总是与特定的处理环境有关。如果应用程序的组件或服务在该特定环境之外直接使用这些系统信息的话,那么这些组件的灵活性和复用性就降低了。在特定环境的外部使用协议相关的API就意味着所有使用这些API的组件都被完全曝露了,并且过于暴露了细节。每一个客户组件因而都与特定的协议紧紧绑定在一起。
解决方案
创建一个环境对象,以一种与协议无关的方式封装系统信息,然后在整个应用程序内部使用这个环境对象。
如下面图5所示,把系统信息封装在一个环境对象中,允许程序的其它部分共享访问,这样就可以避免把应用同特定的协议绑定在一起。比方说,HTML form中每个域都是一个HTTP请求参数,如果使用一个环境对象以一种与协议无关的方式存储这些数据的话,那么数据的转换和验证就会变得容易一些。而且,程序的其它部分也可以直接从环境对象中访问这些信息,不用考虑HTTP协议的问题。如果协议发生变化了,那么只用修改环境对象就行了,应用程序的其它部分不用作任何改动。
图5: 环境对象(Context Object)的UML类图
结果
提高了可复用性和可维护性
既然应用程序的接口独立于特定协议,那么其中所定义的组件和子系统就更通用,可以被各种各样的客户端复用。
提高了易测试性
使用环境对象模式可以帮助清除与特定的Web容器或应用服务器相关的代码。如果限制或者消除了这些依赖,可以更容易进行测试。比如说可以使用一些自动测试工具,例如使用JUnit来进行自动测试。
降低了接口变化的约束
原来应用程序的接口接受大量的系统信息,现在使用环境对象封装这些数据,应用程序只用接受环境对象就可以了。这样可以降低应用程序与特定平台的耦合度,使得以后的修改可以更容易进行。这对于开发应用程序框架非常重要,当然对于开发通用程序也很有价值。
性能的降低
使用环境对象模式要在对象间传递各种数据,因而会导致一定程度性能的下降。不过性能的些微下降与使用环境对象带来的好处,比如应用程序组件复用性提高、应用更容易维护和修改等等比起来,实在是微不足道。
我们在业务层模式中新增加了两个模式,其中之一就是应用服务(Application Service)。这个模式与普通的业务逻辑有关。因为如果使用Session Façades的话,就会导致代码重复,而如果把这些逻辑封装在业务对象(Business Object)的话,又会导致对象数量的剧增。
应用服务(Application Service)
问题
你想跨越几个业务层组件和服务通过聚合来形成一个业务逻辑
服务门面(Service facade),像Session Façade或者POJO Façade几乎不包含业务逻辑,只是对外提供了一个简单的、粗糙的接口。业务对象(Business Object)封装了一组相关业务操作的行为。用例(Use Case)用来协调这些业务对象和服务,而应用程序则用来实现这些用例。然而,你不应该让用例协调业务对象(Business Object)内部的行为,这样会增加依耦合性,降低这些业务对象间的内聚力。同样,你也不应该把业务逻辑加到服务门面(Service facade)上,因为业务逻辑在不同的门面之间潜在地复制了代码,这样会降低通用代码的复用性和可维护性。
解决方案
使用一个应用服务(Application Service)聚合各种行为来提供一个统一的服务层。
应用服务(Application Service)提供了一个实现业务逻辑的中心位置,这个业务逻辑可能封装了各种业务对象和服务。这种实现业务逻辑的方式,可以降低业务对象间的耦合性。使用应用服务(Application Service)模式,你可以把分散的、使用底层业务对象和服务的组件封装成为一个高级的业务逻辑。
即使你的应用程序中没有用到业务对象(Business Object),你也可以使用业务服务(Application Service)模式来提供一个统一的业务逻辑实现层。这种情况下,应用服务(Application Service)可能会包含你程序中实现不同服务必需的所有中间业务逻辑,如果要处理持久性数据的时候,可能还会包括数据访问对象(Data Access Object,DAO)。
图6: 应用服务(Application Service)