在过去几年中,Enterprise JavaBeans™(EJB)确实已经开始对 Java™ 对象设计产生影响。期间,我们看到的最常使用的 EJB 模式之一是Session Facade 概念。这是一个让很多开发者都受益匪浅的既强大又非常简单的概念。然而,我也看到,对这一模式的确切含义及其在实践中的应用,人们仍有很多误解。
为了把这个问题讲得更明白些,我会在本文中讲述 Facade 的一些基本概念以及Session Facade 模式的工作机制,并探讨该模式衍生出来的一些问题。希望能借此澄清一些误解,并帮助开发者正确使用这种模式。
什么是Session Facade?您又为什么需要它?
很多地方都有对Session Facade 模式的清楚描述,也就是 [Sun 2001] 和 [Brown 2000]。我不想照抄那里的全部内容,而打算把它的理论在此作个总结:基本的问题是在 EJB 设计中,EJB 客户机(例如,Servelet、Java 应用程序,等等)不可直接访问 Entity bean。
之所以如此,有以下几个原因:
当依靠 RMI-IIOP 进行跨越网络的调用时运行态的性能会受到极大影响。如果客户机请求一个Entity bean 去表示如包含两项数据(比方说帐户余额和帐户所有者姓名)的银行帐户,则将需要两个网络调用。当大量属性使网络调用成倍增加时,很快这些开销就会变得非常明显。[Monson-Haefel] 中所说的批量访问器(bulk accessors)或许是一种解决方案,所谓批量访问器,就是Entity bean 上的一些方法,它们创建并返回值对象以表示Entity bean 中的数据。它事实上就是 Java VisualAge® 的 CopyHelper Access Beans 采用的解决方案。但是,它有一个令人遗憾的缺陷,就是它假设所有的请求都需要 EJB 中的“所有”数据,结果为用户返回了一些不必要的数据,并导致对更大的值对象进行组织和分解时产生额外开销。
更重要的是,如果您允许 EJB 客户机直接访问Entity bean,那么就要求客户机了解Entity bean 的内部方法,而这已经超出了客户机的应知的范围。例如,操作一个Entity bean 需要知道所涉及到的该实体的关系(关联,继承),这样就把业务模型的所有细节不适当地暴露给了客户机。另外,操作多个Entity bean 会要求使用客户端事务 ? 这是另一个使事情复杂化的因素,这意味着 EJB 可能要被从客户机设计中除去,而不是添加上去。
大多数设计师已经发现为了在 EJB 设计中避免直接访问Entity bean 的解决方案都可以在 [Gamma] 中描述的 Facade 中找到。
[Gamma] 这样描述 Facade 模式:“为子系统中的一套接口提供了一个统一的接口。Facade 定义了一个更高层次的接口,使子系统更容易使用。”1在 EJB 中应用这种思想一般意味着您应该创建一个担当 Facade 的Session EJB,然后把构成子系统的一套Entity bean “包装”起来。这样,客户机就和Entity bean 实现的细节分离开来了,而且不必自己管理事务管理的细节。
但问题是有很多人到此就打住了。然后他们轻松地往下做,开始把Entity bean 包装到Session bean 中,而不考虑 Facade 模式所描述的其它内容以及 EJB 设计中由 Facade 模式衍生出来的问题。这很可能是由于把得到的 Facade 的“二手”信息都当真,而没去研究原始模式的缘故。如果我们确实花了些时间去理解 Facade 衍生的问题,我们将可以看到很多该模式所固有的其它有益的设计可能性。
Facade 模式的要点
[Gamma] 中描述了很多我们应该了解的 Facade 模式的要点。前面几点可在 Facade 模式的“适用性”描述部分找到,它描述了在什么情况下您会需要应用该模式。它们是:“当您想为复杂的子系统提供一个简单接口时……请使用 Facade 模式”和“当您想把子系统分层时……请使用 Facade 模式。使用 Facade 为每一层子系统定义一个入口点。”2
从对 Facade 模式的讨论中,我们可以提炼出两个观点。第一点是 Facade 应该提供子系统的一个抽象视图,而不是简单地把整个子系统本身的 API 直接包装起来。不幸的是,我在实际中多次看到开发者创建的Session bean 把Entity bean home 和Entity bean 对象的全部方法直接包装起来 = =||||,而不提供任何额外的抽象,这是对该模式最可恶的滥用情况之一。请记住,这种思想是想降低整个系统的复杂性,而不是把复杂性转移到另一个对象上。
第二点,也是更微妙的一点,与分层有关。这个观点认为您可以用多重 Facade 来隐藏下层子系统的细节。因此,在这里您可以这样设想,Session Facade 应该在其它 Facade 之上,位于最上层,是对底层业务逻辑细节的进一步抽象。这一点很关键。当您看完下面两条(分别出自 [Gamma] 中论述 Facade 模式的“协作”和“相关模式”部分)叙述后,就会更加清楚这一点:
[Gamma] 中论述 Facade 模式的“协作”和“相关模式”部分:
“客户机通过把请求发送给 Facade,再由 Facade 把请求转发给适当的子系统对象来与子系统通信。”3
“facade 只是对通往子系统对象的接口进行抽象以使它们更易于使用;它不定义新功能。”4
我把这几点总结如下:Facade 不做系统的实际工作;而是委托其他对象轮流做这个工作。由此推理出您必须正确地放置这些对象,以便使该模式能按照您所期望的运行。
这一点是本模式的两种流行表达 [Sun 2000] 和 [Sun 2001] 之间的主要不同之处。第一个版本,即 [Sun 2000],是 J2EE 规划的一部分,它把这种模式称为“Session Entity Facade”。它意在表明“为一堆企业 beans 提供单一的接口”。它描述了这样一种模式,即所有的数据存取都通过Entity bean 来完成,Session bean 则为这些Entity bean 提供接口。现在的问题是 [Sun 2000] 不一定非要以 EJB 为中心。它根本不涉及其它对象类型,并且假设系统中只有 EJB 一类对象。根据我的经验,我认为这会导致根本不能在工程间重用的臃肿的Session对象,而且,在同一个工程内,当需求有一点不同时就会出现问题。
现在,[Sun 2001] 则更通用,也没有上述问题的困扰。它简单地把这种模式称为“Session Facade”。它的解决方案规定您应该“把Session bean 当作 facade 来用,以封装参与工作流的业务对象之间的交互操作的复杂性”。它根本不限制您的业务对象应该为 EJB,因此是一个更加灵活的方法。
Session Facade 的重要规则
那么我们该如何应用这些关于针对会话的 Facade 的规则呢?这对我们的 EJB 设计又意味着什么呢?我在设计Session Facade 时遵循三条基本原则:
它们自己不做实际工作;它们委派其它对象做实际工作。这意味着Session facade 中的每个方法都应该很小(异常处理逻辑不计算在内,代码应为五行或更少)。
它们提供简单的接口。这意味着 facade 方法的数量应相对较少(每个Session bean 中仅有约 24 个)。
它们是底层系统的客户端接口。它们应该把特定于子系统的信息封装起来,并且不应该在不必要的情况下公开它。
那么它的工作机制呢?您还能代理别的哪些类型的对象呢?这又会给您的设计带来什么好处呢?在我的一篇早期论文和 [Brown 2001] 这本书中,我已论述了其中一些问题,在那里可以找到一些详细信息。但,总的来说,在我的多数 EJB 设计中我通常会找到以下四类对象:
值对象(VO)是包含了客户机所请求的数据的、可序列化的 Java bean。它包含Entity bean 和其他数据源所包含的数据的一个子集。它是Session EJB 方法的返回类型。[EJB 2.0] 和 [Sun 2001] 都描述了值对象和值对象的用途。请注意 [Fowler 2001] 称其为“数据传输对象”( Data Transfer Objects )(TO),[Brown 1999] 也使用这个名称。我个人觉得数据传输对象是描述性更好的术语,但不幸的是,Sun 的术语似乎更通用。
对象制造厂 (Factory) [Brown 1999] [Brown 2000] 负责构建值对象。它能完成辨别不同的数据源、创建值对象的实例、填充值对象的实例等等工作。每个 factory 类 都可以从多个数据源中检索数据或更新其中的数据。在您的对象模型中,每个“根”对象都应该有一个 factory 类。(根对象是那些“包含”其它对象的对象。)从某种意义上说,对象 Factory 类在 JDBC 或持久的 Entity bean 子系统上担当 Facade,实现 [Gamma] 中提到的分层原则。
Entity EJB 应该是标准的、企业全局范围内可用的“数据源”。Entity bean 不应包含特定于应用程序的域逻辑,也不应限制为只能在单一应用程序内工作。请注意Entity bean 是可选的,它不是这种体系结构中必需的部分;Factory 可能像 JMS 队列或 JDBC 连接那样简单地直接从数据源获取数据。
Action 对象是Session bean 可能调用的唯一对商业业务进行处理的对象。Action 对象只处理与简单的创建、读取、更新或删除数据无关的商业流程。和对象 Factory 一样,Action 对象也充当内层 Facade。
分享到:
相关推荐
**外观模式(Facade Pattern)**是一种结构型设计模式,它主要解决的是复杂系统或子系统对外暴露一个简单统一的接口,使得客户端无需关心内部复杂的交互细节。这种模式在实际开发中广泛应用,尤其在大型项目中,它能...
设计模式面面观(13):外观模式(Facade Pattern)-结构型模式 http://blog.csdn.net/fanweiwei/archive/2008/04/17/2299641.aspx
外观模式(Facade Pattern)是一种结构型设计模式,用于为复杂子系统提供一个简单的接口。它通过封装系统内部的复杂性,提供一个统一的接口,使得客户端能够更加方便地使用子系统的功能。这种模式通常用于简化代码、...
外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的接口,使得子系统更容易使用。外观模式定义了一个高层接口,使得这一子系统更加容易使用。 压缩包文件代码是一个简单的 ...
在C++中,外观模式(Facade Pattern)同样用于为子系统中的一组接口提供一个统一的接口,使得子系统更容易使用。压缩包文件代码是一个简单的C++实现外观模式的示例。 1、定义各个子系统的类及其接口。 2、定义外观类...
### 外观模式 (Facade Pattern) #### 概述 外观模式是一种重要的设计模式,属于结构型模式之一。其核心在于通过引入一个简单的接口来隐藏一个系统中存在的复杂性,从而简化了客户端对该系统的使用。该模式的主要...
FacadePattern.unitypackage是C#版设计模式中的外观模式,采用unity举例和C#举例,详细说明了外观模式的用法。
门面模式(Facade Pattern)是软件工程中一种常用的结构型设计模式,它的主要目的是提供一个统一的接口,用来简化系统中一组复杂的接口或类的使用。这个模式将复杂的子系统接口封装在一个简单的门面对象中,使得...
在Java中,外观模式(Facade Pattern)同样用于为子系统中的一组接口提供一个统一的接口,使得子系统更容易使用。压缩包文件代码是一个使用Java实现外观模式的简单示例。 1、定义各个子系统的接口及其实现类。 2、...
设计模式面面观(14):享元模式(Facade Pattern)-结构型模式 http://blog.csdn.net/fanweiwei/archive/2008/04/25/2326692.aspx
**包装器外观模式(Wrapper Facade Pattern)** 包装器外观模式是一种软件设计模式,它将一个复杂的组件或者一组组件的接口进行简化,提供一个统一的、更易用的访问方式。这种模式通常用于隐藏底层系统的复杂性,为...
外观模式(Facade Pattern)是设计模式中的一种结构型模式,其主要目的是为了简化客户端与复杂系统之间的交互。在C#编程中,外观模式通常用于减少客户端对子系统组件的依赖,提供一个统一的、高层次的接口,使得...
在给定的文件`FacadePattern.cpp`和`FacadePattern.h`中,我们可以预期看到C++实现的外观模式示例。通常,外观模式包含以下几个关键角色: 1. **外观(Facade)类**:这是对外部世界提供服务的接口,它封装了对子...
7、门面模式FACADE PATTERN 8、适配器模式ADAPTER PATTERN 9、模板方法模式TEMPLATE METHOD PATTERN 10、建造者模式BUILDER PATTERN 11、桥梁模式BRIDGE PATTERN 12、命令模式COMMAND PATTERN 13、装饰模式...
1、基础概念 1.2 类间关系 关联关系(Association) 聚合关系(Aggregation) 组合关系(Composition) 依赖关系(Dependency) 泛化关系(Generalization) ...外观模式(Facade Pattern) 享元模式(Flyweight Pattern)
适配器模式(Adapter Pattern) 提供者模式(Provider Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 原型模式(Prototype Pattern) 责任链模式(Chain of Responsibility Pattern) 中介者模式...
外观模式(Facade Pattern)是设计模式中的一种结构型模式,主要目的是为了解决复杂的系统接口问题,提供一个简单的统一入口,使得客户端可以更方便地使用系统。在Java中,外观模式通常用来隐藏系统的复杂性,对外只...