1、您又为什么需要它?很多地方都有对Session Facade 模式的清楚描述,也就是 Sun 2001 和 Brown 2000。我不想照抄那里的全部内容,而打算把它的理论在此作个总结:基本的问题是在 EJB 设计中,EJB 客户机(例如,Servelet、Java 应用程序,等等)不可直接访问 Entity bean。之所以如此,有以下几个原因: 当依靠 RMI-IIOp 进行跨越网络的调用时运行态的性能会受到极大影响。如果客户机请求一个Entity bean 去表示如包含两项数据(比方说帐户余额和帐户所有者姓名)的银行帐户,则将需要两个网络调用。当大量属性使网络调用成倍增加时,很快这些开销
2、就会变得非常明显。Monson-Haefel 中所说的批量访问器(bulk accessors)或许是一种解决方案,所谓批量访问器,就是Entity bean 上的一些方法,它们创建并返回值对象以表示Entity bean 中的数据。它事实上就是 Java VisualAge 的 CopyHelper Access Beans 采用的解决方案。但是,它有一个令人遗憾的缺陷,就是它假设所有的请求都需要 EJB 中的“所有”数据,结果为用户返回了一些不必要的数据,并导致对更大的值对象进行组织和分解时产生额外开销。 更重要的是,如果您允许 EJB 客户机直接访问Entity bean,那么就要求客户
3、机了解Entity bean 的内部方法,而这已经超出了客户机的应知的范围。例如,操作一个Entity bean 需要知道所涉及到的该实体的关系(关联,继承),这样就把业务模型的所有细节不适当地暴露给了客户机。另外,操作多个Entity bean 会要求使用客户端事务 ? 这是另一个使事情复杂化的因素,这意味着 EJB 可能要被从客户机设计中除去,而不是添加上去。大多数设计师已经发现为了在 EJB 设计中避免直接访问Entity bean 的解决方案都可以在 Gamma 中描述的 Facade 中找到。Gamma 这样描述 Facade 模式:“为子系统中的一套接口提供了一个统一的接口。Fac
4、ade 定义了一个更高层次的接口,使子系统更容易使用。” 1 在 EJB 中应用这种思想一般意味着您应该创建一个担当 Facade 的Session EJB,然后把构成子系统的一套Entity bean “包装”起来。这样,客户机就和Entity bean 实现的细节分离开来了,而且不必自己管理事务管理的细节。但问题是有很多人到此就打住了。然后他们轻松地往下做,开始把Entity bean 包装到Session bean 中,而不考虑 Facade 模式所描述的其它内容以及 EJB 设计中由 Facade 模式衍生出来的问题。这很可能是由于把得到的 Facade 的“二手”信息都当真,而没去研
5、究原始模式的缘故。如果我们确实花了些时间去理解 Facade 衍生的问题,我们将可以看到很多该模式所固有的其它有益的设计可能性。Facade 模式的要点Gamma 中描述了很多我们应该了解的 Facade 模式的要点。前面几点可在 Facade 模式的“适用性”描述部分找到,它描述了在什么情况下您会需要应用该模式。它们是:“当您想为复杂的子系统提供一个简单接口时请使用 Facade 模式”和“当您想把子系统分层时请使用 Facade 模式。使用 Facade 为每一层子系统定义一个入口点。” 2从对 Facade 模式的讨论中,我们可以提炼出两个观点。第一点是 Facade 应该提供子系统的一
6、个抽象视图,而不是简单地把整个子系统本身的 ApI 直接包装起来。不幸的是,我在实际中多次看到开发者创建的Session bean 把Entity bean home 和Entity bean 对象的全部方法直接包装起来,而不提供任何额外的抽象,这是对该模式最可恶的滥用情况之一。请记住,这种思想是想降低整个系统的复杂性,而不是把复杂性转移到另一个对象上。第二点,也是更微妙的一点,与分层有关。这个观点认为您可以用多重 Facade 来隐藏下层子系统的细节。因此,在这里您可以这样设想,Session Facade 应该在其它 Facade 之上,位于最上层,是对底层业务逻辑细节的进一步抽象。这一点
7、很关键。当您看完下面两条(分别出自 Gamma 中论述 Facade 模式的“协作”和“相关模式”部分)叙述后,就会更加清楚这一点: “客户机通过把请求发送给 Facade,再由 Facade 把请求转发给适当的子系统对象来与子系统通信。” 3 “facade 只是对通往子系统对象的接口进行抽象以使它们更易于使用;它不定义新功能。” 4我把这几点总结如下: Facade 不做系统的实际工作;而是委托其他对象轮流做这个工作。由此推理出您必须正确地放置这些对象,以便使该模式能按照您所期望的运行。这一点是本模式的两种流行表达 Sun 2000 和 Sun 2001 之间的主要不同之处。第一个版本,即
8、 Sun 2000,是 J2EE 规划的一部分,它把这种模式称为“Session Entity Facade”。它意在表明“为一堆企业 beans 提供单一的接口”。它描述了这样一种模式,即所有的数据存取都通过Entity bean 来完成,Session bean 则为这些Entity bean 提供接口。现在的问题是 Sun 2000 不一定非要以 EJB 为中心。它根本不涉及其它对象类型,并且假设系统中只有 EJB 一类对象。根据我的经验,我认为这会导致根本不能在工程间重用的臃肿的Session对象,而且,在同一个工程内,当需求有一点不同时就会出现问题。现在,Sun 2001 则更通用,
9、也没有上述问题的困扰。它简单地把这种模式称为“Session Facade”。它的解决方案规定您应该“把Session bean 当作 facade 来用,以封装参与工作流的业务对象之间的交互操作的复杂性”。它根本不限制您的业务对象应该为 EJB,因此是一个更加灵活的方法。Session Facade 的重要规则那么我们该如何应用这些关于针对会话的 Facade 的规则呢?这对我们的 EJB 设计又意味着什么呢?我在设计Session Facade 时遵循三条基本原则: 它们自己不做实际工作;它们委派其它对象做实际工作。这意味着Session facade 中的每个方法都应该很小(异常处理逻辑
10、不计算在内,代码应为五行或更少)。 它们提供简单的接口。这意味着 facade 方法的数量应相对较少(每个Session bean 中仅有约 24 个)。 它们是底层系统的客户端接口。它们应该把特定于子系统的信息封装起来,并且不应该在不必要的情况下公开它。那么它的工作机制呢?您还能代理别的哪些类型的对象呢?这又会给您的设计带来什么好处呢?在我的一篇 早期论文和 Brown 2001 这本书中,我已论述了其中一些问题,在那里可以找到一些详细信息。但,总的来说,在我的多数 EJB 设计中我通常会找到以下四类对象: 值对象是包含了客户机所请求的数据的、可序列化的 Java bean。它包含Entit
11、y bean 和其他数据源所包含的数据的一个子集。它是Session EJB 方法的返回类型。EJB 2.0 和 Sun 2001 都描述了值对象和值对象的用途。请注意 Fowler 2001 称其为“数据传输对象”( Data Transfer Objects ),Brown 1999 也使用这个名称。我个人觉得数据传输对象是描述性更好的术语,但不幸的是,Sun 的术语似乎更通用。 对象制造厂 (Factory)Brown 1999 Brown 2000 负责构建值对象。它能完成辨别不同的数据源、创建值对象的实例、填充值对象的实例等等工作。每个 factory 类 都可以从多个数据源中检索数
12、据或更新其中的数据。在您的对象模型中,每个“根”对象都应该有一个 factory 类。(根对象是那些“包含”其它对象的对象。)从某种意义上说,对象 Factory 类在 JDBC 或持久的 Entity bean 子系统上担当 Facade,实现 Gamma 中提到的分层原则。 Entity EJB应该是标准的、企业全局范围内可用的“数据源”。Entity bean 不应包含特定于应用程序的域逻辑,也不应限制为只能在单一应用程序内工作。请注意Entity bean 是可选的,它不是这种体系结构中必需的部分;Factory 可能像 JMS 队列或 JDBC 连接那样简单地直接从数据源获取数据。
13、Action 对象是Session bean 可能调用的唯一对商业业务进行处理的对象。Action 对象只处理与简单的创建、读取、更新或删除数据无关的商业流程。和对象 Factory 一样,Action 对象也充当内层 Facade。一个 EJB 对象示例描述类似这样的模式遇到的一个问题是,能够使用这种模式的示例都太大,以至于无法包含在模式自身的描述中。尽管如此,我还是要尝试举出如下示例(它显然很简单)来说明一下这些对象看起来是什么样子。假设我们正在为银行构建一个 ATM 系统。这是最老掉牙的 OO 设计问题之一,当然其它很多书籍和论文已经讨论过它,但它确实有足够符合我们要求的有趣特点。通过分
14、析,我们发现了两种 EJB。 从 ATM 到银行的连接表示为Session bean。该 bean 上有一些方法负责处理您通过 ATM 可以完成的交易 ? 存款、取款以及帐户间的资金转移。 帐户表示为Entity bean(我们的示例采用 CMp,但它在我们的示例中实际上并没什么影响)表示。它有返回帐户余额、对帐户进行借贷处理的方法。ATM Session bean 的远程接口如下:package com.ibm.bankexample.ejbs;import com.ibm.bankexample.domain.*;/* * This is the Enterprise Java Bean
15、Remote Interface * for the ATM example. */public interface ATM extends javax.ejb.EJBObject void deposit(java.lang.String accountNumber, double amount) throws java.rmi.RemoteException,com.ibm.bankexample.domain.FactoryException;java.util.Vector getAccounts(java.lang.String userid) void transfer(java.
16、lang.String fromAccount, java.lang.String toAccount, double amount) com.ibm.bankexample.domain.InsufficientFundsException, void withdraw(java.lang.String accountNumber, double amount) 同样地,帐户 EJB 的远程接口如下: * for the Account Entity EJB.*/public interface Account extends javax.ejb.EJBObject void deposit
17、(double amount) throws java.rmi.RemoteException;java.lang.String getAccountNumber() throws java.rmi.RemoteException;double getBalance() throws java.rmi.RemoteException;java.lang.String getUserid() throws java.rmi.RemoteException;void setBalance(double newValue) throws java.rmi.RemoteException;void s
18、etUserid(java.lang.String newUserid) throws java.rmi.RemoteException;void withdraw(double amount) throws java.rmi.RemoteException;现在,我们还发现有另外两种对象类型对我们的系统是有用的。第一种是描述显示在 ATM 机上的帐户信息的值对象。这个类看起来如下所示:public class AccountValue implements java.io.Serializable private java.lang.String accountNumber; private
19、 double balance;当然,AccountValue 类也有作为属性的 getter 和 setter 的方法,但我们暂时不考虑它们。现在,我们基本上有了足够的信息来理解 ATM EJB 的getAccounts()方法的实现。这个方法的实现如下:public java.util.Vector getAccounts(String userid) throws FactoryException AccountFactory fact = new AccountFactory(); Vector result = fact.getAccounts(userid); return res
20、ult;这个方法展示了Session Facade EJB 的方法的标准模式。它找到合适的帮助对象(Action 或 Factory,在本例中是 Factory),调用帮助对象上的业务方法,然后返回结果。如这个方法所指出的,我们需要一个 AccountFactory 类来从 Accounts 构建 AccountValues。这个类的类定义如下:public class AccountFactory private static AccountHome accountHome = null;AccountFactory 的getAccounts(userid)方法的实现如下: try Vect
21、or vect = new Vector(); AccountHome home = getAccountHome(); Enumeration accountRefs = home.findByUserid(userid); while (accountRefs.hasMoreElements() Account acc = (Account) accountRefs.nextElement(); AccountValue valueObject = new AccountValue();valueObject.setAccountNumber(acc.getAccountNumber();
22、 valueObject.setBalance(acc.getBalance(); vect.addElement(valueObject); return vect; catch (Exception e) throw new FactoryException( Cannot generate accounts due to wrapped exception + e);这个方法使用一个高速缓存的 AccountHome 实例,它是从以下方法中获取的:private AccountHome getAccountHome() if (accountHome = null) java.lang.
23、Object homeObject = getInitialContext().lookup(com/ibm/bankexample/ejbs/Account); accountHome = (AccountHome) javax.rmi.portableRemoteObject.narrow( (org.omg.CORBA.Object) homeObject, AccountHome.class); catch (Exception e) / Error getting the home interface System.out.println(Exception + e + in cre
24、ateTimeSheetHome() return accountHome;正如 Brown 2001 和 Gunther 2000 所描述的那样,在 WebSphere 中,高速缓存 EJB home 是一个极好的习惯,因为获取 JNDI InitialContext 和从 InitialContext 获取 EJB Home 需要一段时间。既然您已经看到了Session、Entity和 Factory 如何组合在一起,那我们就来看一个 Action 类的示例。在本例中,我们有一个处理从一个帐户到另一个帐户的资金转移的 Transfer 对象。Transfer 由 ATM EJB 中的tra
25、nsfer()方法的实现中创建,该方法的实现如下:public void transfer(String fromAccount, String toAccount, double amount) throws InsufficientFundsException, FactoryException Transfer trans = new Transfer(); trans.transfer(fromAccount, toAccount, amount);请再次注意同样的流程。不过,这个方法不必从 Action 对象返回值。Transfer 类的定义如下:public class Trans
26、fer private static AccountHome accountHome;transfer()方法的实现如下:public void transfer(String fromAccount, String toAccount, double amount) throws InsufficientFundsException, FactoryException Account from = getAccountHome().findByprimaryKey(new AccountKey(fromAccount); Account to = getAccountHome().findB
27、yprimaryKey(new AccountKey(toAccount); if (from.getBalance() amount) throw new InsufficientFundsException(); to.deposit(amount); from.withdraw(amount);cannot perform transfer. Nested exception is 您已经看到,Transfer 对象中的transfer()方法处理以下细节:定位两个 Account 实体,确保“From”帐户有足够的余额,把转移金额存入“To”帐户,从“From”帐户中提出转移金额。同样地,您可以看到 Action 对象的其它方法可以实现您系统中的其它业务规则。使用 EJB 对象的原因那么为什么我们需要这第二层对象呢?难道我们从 CORBA 和 RMI 转到 Enterprise JavaBean 就使事情更简单了吗?为什么不把所有的逻辑都放到 EJB 中呢?这有几个原因。第一个也是最重要的原因是这是一个分层应用程序。在单个对象中放置太多工作从来不是一个好主意。如果您用这种方式来布置由 EJB 调用的对象,可以带来以
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1