session facade.docx

上传人:b****5 文档编号:8168991 上传时间:2023-01-29 格式:DOCX 页数:14 大小:31.57KB
下载 相关 举报
session facade.docx_第1页
第1页 / 共14页
session facade.docx_第2页
第2页 / 共14页
session facade.docx_第3页
第3页 / 共14页
session facade.docx_第4页
第4页 / 共14页
session facade.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

session facade.docx

《session facade.docx》由会员分享,可在线阅读,更多相关《session facade.docx(14页珍藏版)》请在冰豆网上搜索。

session facade.docx

sessionfacade

SessionFacade的规则和模式

KyleBrown(brownkyl@),Java执行设计师,IBMWebSphereService

简介:

在过去几年中,EnterpriseJavaBeans™(EJB)确实已经开始对Java™对象设计产生影响。

期间,我们看到的最常使用的EJB模式之一是SessionFacade概念。

发布日期:

2001年11月01日

级别:

初级

访问情况:

2160次浏览

评论:

0(查看|添加评论-登录)

平均分(1个评分)

为本文评分

在过去几年中,EnterpriseJavaBeans™(EJB)确实已经开始对Java™对象设计产生影响。

期间,我们看到的最常使用的EJB模式之一是SessionFacade概念。

这是一个让很多开发者都受益匪浅的既强大又非常简单的概念。

然而,我也看到,对这一模式的确切含义及其在实践中的应用,人们仍有很多误解。

为了把这个问题讲得更明白些,我会在本文中讲述Facade的一些基本概念以及SessionFacade模式的工作机制,并探讨该模式衍生出来的一些问题。

希望能借此澄清一些误解,并帮助开发者正确使用这种模式。

什么是SessionFacade?

您又为什么需要它?

很多地方都有对SessionFacade模式的清楚描述,也就是[Sun2001]和[Brown2000]。

我不想照抄那里的全部内容,而打算把它的理论在此作个总结:

基本的问题是在EJB设计中,EJB客户机(例如,Servelet、Java应用程序,等等)不可直接访问Entitybean。

之所以如此,有以下几个原因:

∙当依靠RMI-IIOp进行跨越网络的调用时运行态的性能会受到极大影响。

如果客户机请求一个Entitybean去表示如包含两项数据(比方说帐户余额和帐户所有者姓名)的银行帐户,则将需要两个网络调用。

当大量属性使网络调用成倍增加时,很快这些开销就会变得非常明显。

[Monson-Haefel]中所说的批量访问器(bulkaccessors)或许是一种解决方案,所谓批量访问器,就是Entitybean上的一些方法,它们创建并返回值对象以表示Entitybean中的数据。

它事实上就是JavaVisualAge®的CopyHelperAccessBeans采用的解决方案。

但是,它有一个令人遗憾的缺陷,就是它假设所有的请求都需要EJB中的“所有”数据,结果为用户返回了一些不必要的数据,并导致对更大的值对象进行组织和分解时产生额外开销。

∙更重要的是,如果您允许EJB客户机直接访问Entitybean,那么就要求客户机了解Entitybean的内部方法,而这已经超出了客户机的应知的范围。

例如,操作一个Entitybean需要知道所涉及到的该实体的关系(关联,继承),这样就把业务模型的所有细节不适当地暴露给了客户机。

另外,操作多个Entitybean会要求使用客户端事务?

这是另一个使事情复杂化的因素,这意味着EJB可能要被从客户机设计中除去,而不是添加上去。

大多数设计师已经发现为了在EJB设计中避免直接访问Entitybean的解决方案都可以在[Gamma]中描述的Facade中找到。

[Gamma]这样描述Facade模式:

“为子系统中的一套接口提供了一个统一的接口。

Facade定义了一个更高层次的接口,使子系统更容易使用。

”1在EJB中应用这种思想一般意味着您应该创建一个担当Facade的SessionEJB,然后把构成子系统的一套Entitybean“包装”起来。

这样,客户机就和Entitybean实现的细节分离开来了,而且不必自己管理事务管理的细节。

但问题是有很多人到此就打住了。

然后他们轻松地往下做,开始把Entitybean包装到Sessionbean中,而不考虑Facade模式所描述的其它内容以及EJB设计中由Facade模式衍生出来的问题。

这很可能是由于把得到的Facade的“二手”信息都当真,而没去研究原始模式的缘故。

如果我们确实花了些时间去理解Facade衍生的问题,我们将可以看到很多该模式所固有的其它有益的设计可能性。

Facade模式的要点

[Gamma]中描述了很多我们应该了解的Facade模式的要点。

前面几点可在Facade模式的“适用性”描述部分找到,它描述了在什么情况下您会需要应用该模式。

它们是:

“当您想为复杂的子系统提供一个简单接口时……请使用Facade模式”和“当您想把子系统分层时……请使用Facade模式。

使用Facade为每一层子系统定义一个入口点。

”2

从对Facade模式的讨论中,我们可以提炼出两个观点。

第一点是Facade应该提供子系统的一个抽象视图,而不是简单地把整个子系统本身的ApI直接包装起来。

不幸的是,我在实际中多次看到开发者创建的Sessionbean把Entitybeanhome和Entitybean对象的全部方法直接包装起来,而不提供任何额外的抽象,这是对该模式最可恶的滥用情况之一。

请记住,这种思想是想降低整个系统的复杂性,而不是把复杂性转移到另一个对象上。

第二点,也是更微妙的一点,与分层有关。

这个观点认为您可以用多重Facade来隐藏下层子系统的细节。

因此,在这里您可以这样设想,SessionFacade应该在其它Facade之上,位于最上层,是对底层业务逻辑细节的进一步抽象。

这一点很关键。

当您看完下面两条(分别出自[Gamma]中论述Facade模式的“协作”和“相关模式”部分)叙述后,就会更加清楚这一点:

∙“客户机通过把请求发送给Facade,再由Facade把请求转发给适当的子系统对象来与子系统通信。

”3

∙“facade只是对通往子系统对象的接口进行抽象以使它们更易于使用;它不定义新功能。

”4

我把这几点总结如下:

Facade不做系统的实际工作;而是委托其他对象轮流做这个工作。

由此推理出您必须正确地放置这些对象,以便使该模式能按照您所期望的运行。

这一点是本模式的两种流行表达[Sun2000]和[Sun2001]之间的主要不同之处。

第一个版本,即[Sun2000],是J2EE规划的一部分,它把这种模式称为“SessionEntityFacade”。

它意在表明“为一堆企业beans提供单一的接口”。

它描述了这样一种模式,即所有的数据存取都通过Entitybean来完成,Sessionbean则为这些Entitybean提供接口。

现在的问题是[Sun2000]不一定非要以EJB为中心。

它根本不涉及其它对象类型,并且假设系统中只有EJB一类对象。

根据我的经验,我认为这会导致根本不能在工程间重用的臃肿的Session对象,而且,在同一个工程内,当需求有一点不同时就会出现问题。

现在,[Sun2001]则更通用,也没有上述问题的困扰。

它简单地把这种模式称为“SessionFacade”。

它的解决方案规定您应该“把Sessionbean当作facade来用,以封装参与工作流的业务对象之间的交互操作的复杂性”。

它根本不限制您的业务对象应该为EJB,因此是一个更加灵活的方法。

SessionFacade的重要规则

那么我们该如何应用这些关于针对会话的Facade的规则呢?

这对我们的EJB设计又意味着什么呢?

我在设计SessionFacade时遵循三条基本原则:

∙它们自己不做实际工作;它们委派其它对象做实际工作。

这意味着Sessionfacade中的每个方法都应该很小(异常处理逻辑不计算在内,代码应为五行或更少)。

∙它们提供简单的接口。

这意味着facade方法的数量应相对较少(每个Sessionbean中仅有约24个)。

∙它们是底层系统的客户端接口。

它们应该把特定于子系统的信息封装起来,并且不应该在不必要的情况下公开它。

那么它的工作机制呢?

您还能代理别的哪些类型的对象呢?

这又会给您的设计带来什么好处呢?

在我的一篇早期论文和[Brown2001]这本书中,我已论述了其中一些问题,在那里可以找到一些详细信息。

但,总的来说,在我的多数EJB设计中我通常会找到以下四类对象:

∙值对象是包含了客户机所请求的数据的、可序列化的Javabean。

它包含Entitybean和其他数据源所包含的数据的一个子集。

它是SessionEJB方法的返回类型。

[EJB2.0]和[Sun2001]都描述了值对象和值对象的用途。

请注意[Fowler2001]称其为“数据传输对象”(DataTransferObjects),[Brown1999]也使用这个名称。

我个人觉得数据传输对象是描述性更好的术语,但不幸的是,Sun的术语似乎更通用。

∙对象制造厂(Factory)[Brown1999][Brown2000]负责构建值对象。

它能完成辨别不同的数据源、创建值对象的实例、填充值对象的实例等等工作。

每个factory类都可以从多个数据源中检索数据或更新其中的数据。

在您的对象模型中,每个“根”对象都应该有一个factory类。

(根对象是那些“包含”其它对象的对象。

)从某种意义上说,对象Factory类在JDBC或持久的Entitybean子系统上担当Facade,实现[Gamma]中提到的分层原则。

∙EntityEJB应该是标准的、企业全局范围内可用的“数据源”。

Entitybean不应包含特定于应用程序的域逻辑,也不应限制为只能在单一应用程序内工作。

请注意Entitybean是可选的,它不是这种体系结构中必需的部分;Factory可能像JMS队列或JDBC连接那样简单地直接从数据源获取数据。

∙Action对象是Sessionbean可能调用的唯一对商业业务进行处理的对象。

Action对象只处理与简单的创建、读取、更新或删除数据无关的商业流程。

和对象Factory一样,Action对象也充当内层Facade。

一个EJB对象示例

描述类似这样的模式遇到的一个问题是,能够使用这种模式的示例都太大,以至于无法包含在模式自身的描述中。

尽管如此,我还是要尝试举出如下示例(它显然很简单)来说明一下这些对象看起来是什么样子。

假设我们正在为银行构建一个ATM系统。

这是最老掉牙的OO设计问题之一,当然其它很多书籍和论文已经讨论过它,但它确实有足够符合我们要求的有趣特点。

通过分析,我们发现了两种EJB。

∙从ATM到银行的连接表示为Sessionbean。

该bean上有一些方法负责处理您通过ATM可以完成的交易?

存款、取款以及帐户间的资金转移。

∙帐户表示为Entitybean(我们的示例采用CMp,但它在我们的示例中实际上并没什么影响)表示。

它有返回帐户余额、对帐户进行借贷处理的方法。

ATMSessionbean的远程接口如下:

packagecom.ibm.bankexample.ejbs;

importcom.ibm.bankexample.domain.*;

/**

*ThisistheEnterpriseJavaBeanRemoteInterface

*fortheATMexample.

*/

publicinterfaceATMextendsjavax.ejb.EJBObject{

voiddeposit(java.lang.StringaccountNumber,doubleamount)

throwsjava.rmi.RemoteException,

com.ibm.bankexample.domain.FactoryException;

java.util.VectorgetAccounts(java.lang.Stringuserid)

throwsjava.rmi.RemoteException,

com.ibm.bankexample.domain.FactoryException;

voidtransfer(java.lang.StringfromAccount,java.lang.StringtoAccount,doubleamount)

throwsjava.rmi.RemoteException,

com.ibm.bankexample.domain.InsufficientFundsException,

com.ibm.bankexample.domain.FactoryException;

voidwithdraw(java.lang.StringaccountNumber,doubleamount)

throwsjava.rmi.RemoteException,

com.ibm.bankexample.domain.InsufficientFundsException,

com.ibm.bankexample.domain.FactoryException;

}

同样地,帐户EJB的远程接口如下:

packagecom.ibm.bankexample.ejbs;

/**

*ThisistheEnterpriseJavaBeanRemoteInterface

*fortheAccountEntityEJB.

*/

publicinterfaceAccountextendsjavax.ejb.EJBObject{

voiddeposit(doubleamount)throwsjava.rmi.RemoteException;

java.lang.StringgetAccountNumber()throwsjava.rmi.RemoteException;

doublegetBalance()throwsjava.rmi.RemoteException;

java.lang.StringgetUserid()throwsjava.rmi.RemoteException;

voidsetBalance(doublenewValue)throwsjava.rmi.RemoteException;

voidsetUserid(java.lang.StringnewUserid)throwsjava.rmi.RemoteException;

voidwithdraw(doubleamount)throwsjava.rmi.RemoteException;

}

现在,我们还发现有另外两种对象类型对我们的系统是有用的。

第一种是描述显示在ATM机上的帐户信息的值对象。

这个类看起来如下所示:

publicclassAccountValueimplementsjava.io.Serializable{

privatejava.lang.StringaccountNumber;

privatedoublebalance;

}

当然,AccountValue类也有作为属性的getter和setter的方法,但我们暂时不考虑它们。

现在,我们基本上有了足够的信息来理解ATMEJB的getAccounts()方法的实现。

这个方法的实现如下:

publicjava.util.VectorgetAccounts(Stringuserid)throwsFactoryException{

AccountFactoryfact=newAccountFactory();

Vectorresult=fact.getAccounts(userid);

returnresult;

}

这个方法展示了SessionFacadeEJB的方法的标准模式。

它找到合适的帮助对象(Action或Factory,在本例中是Factory),调用帮助对象上的业务方法,然后返回结果。

如这个方法所指出的,我们需要一个AccountFactory类来从Accounts构建AccountValues。

这个类的类定义如下:

publicclassAccountFactory{

privatestaticAccountHomeaccountHome=null;

}

AccountFactory的getAccounts(userid)方法的实现如下:

publicjava.util.VectorgetAccounts(Stringuserid)throwsFactoryException{

try{

Vectorvect=newVector();

AccountHomehome=getAccountHome();

EnumerationaccountRefs=home.findByUserid(userid);

while(accountRefs.hasMoreElements()){

Accountacc=(Account)accountRefs.nextElement();

AccountValuevalueObject=newAccountValue();

valueObject.setAccountNumber(

acc.getAccountNumber());

valueObject.setBalance(acc.getBalance());

vect.addElement(valueObject);

}

returnvect;

}catch(Exceptione){

thrownewFactoryException(

"Cannotgenerateaccountsduetowrappedexception"+e);

}

}

这个方法使用一个高速缓存的AccountHome实例,它是从以下方法中获取的:

privateAccountHomegetAccountHome(){

if(accountHome==null){

try{

java.lang.ObjecthomeObject=getInitialContext().lookup(

"com/ibm/bankexample/ejbs/Account");

accountHome=

(AccountHome)javax.rmi.portableRemoteObject.narrow(

(org.omg.CORBA.Object)homeObject,

AccountHome.class);

}catch(Exceptione){

//Errorgettingthehomeinterface

System.out.println(

"Exception"+e+"increateTimeSheetHome()");

}

}

returnaccountHome;

}

正如[Brown2001]和[Gunther2000]所描述的那样,在WebSphere®中,高速缓存EJBhome是一个极好的习惯,因为获取JNDIInitialContext和从InitialContext获取EJBHome需要一段时间。

既然您已经看到了Session、Entity和Factory如何组合在一起,那我们就来看一个Action类的示例。

在本例中,我们有一个处理从一个帐户到另一个帐户的资金转移的Transfer对象。

Transfer由ATMEJB中的transfer()方法的实现中创建,该方法的实现如下:

publicvoidtransfer(StringfromAccount,StringtoAccount,doubleamount)

throwsInsufficientFundsException,FactoryException{

Transfertrans=newTransfer();

trans.transfer(fromAccount,toAccount,amount);

}

请再次注意同样的流程。

不过,这个方法不必从Action对象返回值。

Transfer类的定义如下:

publicclassTransfer{

privatestaticAccountHomeaccountHome;

}

transfer()方法的实现如下:

publicvoidtransfer(StringfromAccount,StringtoAccount,

doubleamount)throwsInsufficientFundsException,FactoryException{

try{

Accountfrom=getAccountHome().findByprimaryKey(

newAccountKey(fromAccount));

Accountto=getAccountHome().findByprimaryKey(

newAccountKey(toAccount));

if(from.getBalance()

thrownewInsufficientFundsException();

to.deposit(amount);

from.withdraw(amount);

}catch(Exceptione){

thrownewFactoryException(

"cannotperformtransfer.Nestedexceptionis"+e);

}

}

您已经看到,Transfer对象中的transfer()方法处理以下细节:

定位两个Account实体,确保“From”帐户有足够的余额,把转移金额存入“To”帐户,从“From”帐户中提出转移金额。

同样地,您可以看到Action对象的其它方法可以实现您系统中的其它业务规则。

使用EJB对象的原因

那么为什么我们需要这第二层对象呢?

难道我们从CORBA和RMI转到EnterpriseJavaBean就使事情更简单了吗?

为什么不把所有的逻辑都放到EJB中呢?

这有几个原因。

第一个也是最重要的原因是这是一个分层应用程序。

在单个对象中放置太多工作从来不是一个好主意。

如果您用这种方式来布置由EJB调用的对象,可以带来以

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1