翻译文章的第一章 http://sc.hnu.cn/~j2ee/ejbdesignpatterns.pdf
sean_gao(大胃) 翻译了其中一部分了!

解决方案 »

  1.   

    sean_gao(大胃) 同志,你们公司几点上班啊?
      

  2.   

    to sean_gao(大胃)
        谢谢,你也快了吧!
      

  3.   

    ---------------------------------------------------------------------------
    EJB命令模式
    ---------------------------------------------------------------------------EJB的客户需要执行商务逻辑以完成用例。[开发者如何以一种轻量的方式实现一个用例的商务逻辑,将客户端从EJB分离开来并在
    单个事务和单次网络调用中执行用例?] * * *我们在设计一个EJB系统时的一个关键的架构级决策是,将商务逻辑放在哪儿。一个用
    例的商务逻辑是指导向域模型中正确合适的方法的逻辑,或者是指执行跨多个其它实体
    bean/会话bean的逻辑(工作流逻辑)。将商务逻辑放在客户端(servlet、applet等等)会带来严重的负面效果,影响性能和
    可维护性,这一点在会话面板模式中解释过。这些问题可以通过使用使用会话面板模式
    解决,将商务逻辑放在会话bean的方法中,这些方法一一对应工作单元或者用例。这样
    做使得客户端独立于服务器端的对象模型,且用例在单个事务、单次网络往返中得以执
    行。会话面板模式本身是EJB开发中的基本模式,但同样有自身的缺陷。直接从客户端访问
    会话面板会造成大型项目中客户端团队和服务器端团队之间的相互依赖,并且与EJB的
    高度耦合使得客户端的代码复杂化,正如商务代理模式中提到的。这些问题可以通过使
    用商务代理得到缓解:商务代理模式增加一个新的对象层封装所有对EJB层的访问。商
    务逻辑有助于保持客户端代码简单,并最大限度减轻客户端和服务器端的相互依赖。于是,会话面板模式同商务代理模式一道,提供了一组编写商务逻辑的最佳实践:将客
    户端从服务器端的实现细节独立出来,并使得用例在单次网络调用和单个事务中执行。
    同样,这也有利弊(这里指弊端--译者注):[减慢的开发进程] 由于用例逻辑(经常会变化)在会话bean中执行,每当用例需要改
    动时(比如,为一个方法增加参数或者返回额外的属性),会话bean中实现该用例的方
    法可能就需要改动。修改会话bean的进程并非无足轻重:改动通常需要编辑3个不同的
    文件(接口、bean类、部署描述文件)以及重新部署到EJB服务器并可能需要重启服务
    器。另外,在客户端封装该会话bean的商务代理通常也需要修改。[在大型项目中分工更难] 根据在一个项目上分工方式的不同,会话面板通常会成为不
    同团队和开发人员需要下力气解决的瓶颈,因为它很可能随着项目的进展而频繁改变。[在大公司中服务器资源通常是由一小组人员控制] 对于大型的、拥有现成的、运行中
    的EJB的公司,其他项目的开发人员很难修改已有的类。简单的讲,通过会话面板和商务代理来开发可能会造成较长的"修改-发布-测试"回路,
    这一点在大项目中会成为瓶颈。这个问题的关键在于商务逻辑被放在会话EJB层,这对
    开发来说可以是十分重量级的。因此:[用命令模式将商务逻辑包装到轻量级的命令bean,将客户端与EJB分离,在单次网络
    调用中执行,并作为EJB层的面板(外观)。]命令bean仅仅是普通的,拥有get、set、execute方法的Java类,如在原始的命令模式
    中提到的那样(Gamma, et al., 1995)。当应用到EJB时,命令模式提供了一个轻量
    级的解决方案来实现与会话面板和商务代理模式相同的好处:作为面板,隐藏了EJB层
    的对象模型;在单个事务单次网络调用中执行用例;客户端和EJB的完全分离。命令模
    式通过给客户端提供本地交互,但实际上在远程EJB服务器上执行,对客户端透明的方
    式来实现上述优点。命令被用于封装应用程序中独立的工作单元。类似placeOrder、transferFunds等等这
    样的用例可以将其商务/工作流逻辑一个专门用于那个用例的命令中,如图1.7所示。客户端与命令的交互非常简单。一旦客户端得到一个命令(或者是通过创建,或者是
    通过工厂,视具体实现而定),它只需要简单的set属性到命令中,直到命令包含了所
    有执行用例所需的数据。这个时候客户端就可以调用命令(对象)的execute方法,然
    后当它取得了所有执行命令/用例后得到的数据后,就可以简单的通过对命令对象get
    来取得结果。当客户端执行命令时,有用的事情发生在后台。命令并非在本地执行,而是传送到远程
    的EJB服务器并且在EJB服务器的JVM中执行,因此所有该用例执行过程中被命令调用的
    EJB都出现在EJB服务器内部。当命令执行完毕,它被返回到客户端,客户端这时就可以
    调用其get方法取得数据。通过在EJB内部执行命令,一个用例可以在单独的事务中执行。
    具体实现机制会在稍后对该模式的讨论中进行解释。拿trasferFunds例子来说,客户端可以设定提款账户的ID、存款账户的ID、以及划账金
    额。在调用transferFunds命令的exec方法后,客户端可以取得最终的账户余额信息,
    如图1.8所示。<图略>随IBM的电子商务模型WebSphere发布的命令框架可能最全面的命令模式的具体实现之一。
    实现EJB命令模式有很多不同的途径,但是有3个元素是共通的:[命令bean] 这是一个简单的JavaBean类,具备get/set方法和一个包含了用以执行用例
    的商务逻辑的execute方法。命令bean是命令模式中唯一一块需要应用程序开发者编写
    的模块,以下提到的其他组件可以跨项目重用。[客户端路由逻辑] 通常是一个类框架负责接收命令及发送到远程EJB服务器。这个路由
    逻辑对于客户端而言通常是不可见的,是通过调用命令对象的execute方法触发的。这
    个路由逻辑/框架是一组通用的类,可以跨项目重用。[远程命令服务器] 这是一个服务,简单的接收命令并执行。具体到EJB,命令服务器类
    是一个无状态的会话bean,将命令作为参数接收并本地执行。这个CommandServer同样
    也是通用的可以完全跨项目重用。客户端和这些组件的交互如图1.9所示。在本例中,客户端调用路由逻辑组件的execute-
    Command方法。在IBM的命令框架中,客户端只需要调用命令对象本身的execute方法即可,
    因为这个调用最终会被命令对象的父类接收,这个父类正是路由逻辑框架的一部分。在后台,CommandExector代理了所有对EJBCommandTarget的调用(没有在图1.9中表示
    出来,因为这是路由逻辑的一部分),其中的编码包含了EJB的信息以及如何向Command-
    Server这个无状态会话bean发送命令。CommandServer接到命令后,会简单的调用命令对
    象的execute方法,于是命令中的商务逻辑得到执行。使用命令模式的好处有:[快速应用程序开发(RAD),轻量级的开发/部署进程] 将用例写作命令bean使得部署和测
    试相对于将其写作会话bean的方法而言要显著的容易和简单。经常性的修改仅需要针对
    普通的Java类而不是完整的EJB。[商务逻辑和表示逻辑的分离] 命令作为服务器端具体对象模型的面板,将商务逻辑封装
    在命令内部,只暴露一组简单的接口供客户端使用。这样的分离使得客户端和服务器端
    可以分别实现。[促使用例在单个回路中执行] 由于命令实际上是在服务器端执行,仅需要一次网络调用
    (和一个事务)来完成一个复杂的用例。[将客户和EJB分离] 客户端被完全与服务器实现细节分离:客户端所看到的仅仅是命令
    bean,而这个命令bean看上去是一个本地类。[命令可以在本地执行或者产生傻瓜数据] 在项目开始时可以创建空的或伪造的命令对象
    以使得表示层开发人员可以独立于商务逻辑/EJB团队进行编写、编译和测试。<图略>
      

  4.   

    从很多方面来看命令模式听上去都是一个终极的解决方案,集会话面板和商务代理模式
    的优势于一身,只是层次结构更加轻量。但是在这些优势的同时,也存在弊端:[很粗糙的事务控制] 由于命令对象只是简单的JavaBean,并没有一个方法可以自动的
    标记为在特定的事务设置或者隔离等级下运行,像会话bean那样。命令只能在执行它们
    的CommandServer的事务设置下运行。解决的方法是以不同的JDNI名称和事务设置部署
    多个命令服务器bean(在部署描述文件中配置)。路由逻辑组件需要配置成发送特定的
    命令到特定的服务器。也就是说,我们可以发送所有只读的命令到没有事务处理的会话
    bean,而把那些更新命令发送到一个配置了tx_requires和serializable隔离等级的命
    令服务器。[命令是无状态的] 命令对象不能在执行它会话bean中储存任何状态信息。因此在命令
    模式中无法将状态保存到EJB层。[笨拙的错误处理] 由于命令框架式通用的,从一个命令中只能抛出CommandException
    异常。这就意味着应用程序异常,如NoMoneyInAcountException需要被包装在一个
    CommandException对象中被捕获。客户端于是需要检查命令对象内部以查找特定的异常。
    由于异常不是显示声明的,客户端就失去了编译期异常处理的检查带来的好处。[在大型项目中命令可能会变得无法控制] 一个大型的项目可能会包含数千条命令,其
    中很大的部分都拥有重复的商务逻辑,尤其是当不同的项目组使用同一个后端域模型时
    这个问题显得尤为突出。这使得维护商务逻辑变得困难了许多许多。会话面板模式则不
    同:用例被实现为会话bean的方法,很好的组织到少量的会话bean中。在大型项目中,
    这种类层次结构很可能构成严重的问题。[CommandServer ejb-jar同命令bean和其他EJB紧密耦合] 由于命令bean在CommandServer
    会话bean的环境中执行,命令bean类需要同CommandServer会话bean一起发布(在同一个
    ejb-jar或者EAR中)以便命令bean可以被反序列化并执行。也就是说一旦命令bean被修改
    了,CommandServer会话bean的EAR或者ejb-jar就需要重新部署(这样CommandServer的类
    装载器才能读取所有包含的命令bean的新版本)以便测试这个改动,甚至如果你的应用程
    序服务器不支持"热部署"(类似热插拔),整个服务器都需要重启。还有,命令bean要能
    看到所有它们在执行商务逻辑中可能用到的本地和远程接口。这就要求,要么CommandServer
    同这些命令bean可能用到的所有EJB在同一个EAR中发布,要么这些可能用到的EJB的接口
    被打包在命令服务器的ejb-jar中。命令模式和会话面板模式都提供了两个重要的好处:它们作为面板(服务器端对象模型的
    外观)存在,以及在单个网络回路中执行。除此以外,命令模式优于会话面板模式的主要
    优势在于:它使客户端和EJB分离开来,而要实现这一点,会话面板模式必须同商务代理模
    式一道实现。那么开发人员怎样在这些模式中取舍呢?我们可以把命令想象成一种"更便宜
    的"会话bean。它们更轻量,初始开发进程更快,缺陷是随着时间推移可维护性的降低。[相关模式]命令模式(Gamma, et al., 1995)
    数据传输HashMap
      

  5.   

    ---------------------------------------------------------------------------
    数据传输对象工厂模式
    ---------------------------------------------------------------------------对于一个使用数据传输对象(DTO)的J2EE系统而言,其DTO层经常会有改动。[怎样实现数据传输对象的创建和销毁逻辑以便最大限度的减小DTO层的经常性改动对系统
    其他部分的影响?] * * *数据传输对象倾向于经常改变。一旦域对象发生改变(如向实体bean增加新的属性等),
    域DTO也要随之改变。自定义的DTO只是针对特定用例的用于在网络上传递数据的数据持有
    者,它们随着你的表示层的经常性改变而改变。一个中到大型的应用程序很可能会数十甚
    至上百个不同的数据传输对象,每个对象都需要特殊的逻辑来创建。这样就出现了一个关
    键性的问题:我们应该怎样以及在哪里实现这个逻辑,从而将数据传输对象的变化和系统
    其他部分分隔开来?在EJB 1.X的应用程序中常见的解决方法是把getXXXDTO/setXXXDTO方法直接放在实体bean
    中。这种情况下,实体bean负责填充这个数据对象模型,以及根据setDTO的属性更新自己。
    这种方式的问题在于它将数据传输对象层同实体bean层紧密地耦合在一起。换句话说,把
    用例特定的数据传输对象的创建代码放在实体bean中这样的做法在中到大型的应用程序中
    会造成实体bean和客户端严重的相互依赖。每当一个Web页改动了,需要数据模型的不同的
    视图时,你将要向实体bean增加一个新的方法,然后重新将远程接口分发到任何使用它们
    的客户。实体bean应该是可以重用的商务组件,能够单独组装成应用程序。为了构建真正可重用的
    商务组件,很重要的一点是保持应用程序逻辑和商务逻辑之间的严格界限,使二者能独立
    实现或改进。我们需要另一种方案来创建和使用实体bean,一种可以将DTO相关逻辑和系
    统中其他组件分离开来的方案。因此:[将创建和使用数据传输对象的任务交给数据传输对象工厂来完成。]数据传输对象工厂将与数据传输对象相关的逻辑(应用程序域的一部分)同系统中其他组
    件如实体bean(商务域的一部分)分离开来。当需要新的视图或者不同的服务器端数据的
    子集时,新的DTO创建方法可以加入到一个DTOFactory,而不是放在实体bean中。这些新
    的方法会与实体bean层(或者其他数据源如connector和直接JDBC等)交互,调用getter
    方法,遍历层次关系,来生成域或自定义的数据传输对象。这样做的好处是实体bean本身
    并不需要知道这些不同的视图,实际上,实体bean不需要任何代码修改。举个例子来说,考虑一个汽车应用程序,让用户浏览汽车和厂家信息。这个应用程序于是
    具有一个包含了一个Car和一个Manufacturer的实体bean(当然还有其他的)的域模型。
    这样的一个应用程序需要具备一个包含很多页面的UI以便让用户能够浏览汽车和厂家的不
    同属性,包括Car的各种属性(如引擎属性、车身属性、底盘等等)的不同子集,以及跨
    多个实体bean的数据(关于一种车型及其生产厂家的信息等)。这些不同的数据集应该通
    过自定义的DTO传输到客户端。这些用于创建不同DTO的方法将放在一个如图1.10所示的
    DTOFactory中,而不是放在Car或者Manufacturer实体bean中。现在这个CarDTOFactory成为了用例特定的DTO逻辑的唯一所在,有助于将客户端从域模型
    中分离出来。域模型中的实体bean现在就可以自由的成为域对象,只暴露商务方法给客户
    而不是难看的DTO get/set逻辑,这个逻辑与具体的域模型所代表的的商务概念一点也不
    沾边。<图略>根据工厂的客户端是会话bean还是非EJB客户(如servlet),有两种实现DTO工厂模式的
    基本途径。在会话bean面板后面使用时,DTO工厂可以通过普通的Java类在方法中简单的
    存放针对不同数据传输对象的创建/使用逻辑。这种类型的工厂很易于被重用,因为其生
    成的数据传输对象可以在不同的会话bean与/或不同的项目中使用。当从非EJB客户调用时,DTO工厂应该作为一个无状态的会话bean实现。这种情况下典型的
    客户与数据传输对象的交互如图1.11所示。在这里,一个servlet客户需要获取一个名叫
    CarAndManufacturerDTO的自定义DTO,因此它向一个CarDTOFactory询求这个对象。于是
    CarDTOFactory创建并通过本地接口调用Car实体bean和相应的Manufacturer实体bean的
    get方法来填充这个DTO。数据传输对象工厂可以被用于创建任何类型的DTO。甚至可以创建用于映射服务器端实体
    bean对象模型的不同"切片"的复杂的"聚合式"DTO(包含其他域DTO的域DTO)层次结构。
    复杂的数据传输对象层次结构可以通过显式的编写能够浏览(复制)用例特定的实体bean
    层次结构的"切片"的代码。这些DTO层次结构可以在单次网络调用中在服务器端创建并返
    回客户端。这样做的一个很重要的好处是,我们的应用程序中的实体bean现在完全可以重用了。例如,
    想象在同一个公司中两个不同的团队在编写不同的应用程序。这两个团队可以通过使用独
    立的数据传输对象工厂重用相同的实体bean组件。他们可以分别实现自己的DTO工厂生成
    用例特定的实体bean状态的DTO"切片"(两个团队生成的工厂是相互独立的),从而实现
    完全的重用。通过保有自己的DTO工厂,他们也可以开发、发布完全独立的应用程序。这
    个概念在图1.12中有说明。注意,数据传输对象模式并不隐含的要求为每一个实体bean类创建一个DTO工厂。比方说,
    你并非一定需要为一个Car实体bean创建一个CarDTOFactory。这样做会产生过多的VO工厂。
    当需求允许时,为一整组实体bean及其他服务器端数据提供一个DTO工厂可能会更直接。DTO工厂提供了一个从服务器端读取数据的方式,但是更新数据呢?与读取数据类似的技巧
    可以用来实现数据更新。也就是说,客户端可以传递一个域DTO或者是一个自定义的DTO到
    服务器端,对实体bean或者其他服务器端的数据执行创建、读取、更新和删除(CRUD)操
    作。对于域DTO(通常是可改动的)而言,客户端在本地将更新执行到DTO,然后通过传递域DTO
    到一个DTO工厂的updateXXXEntity方法来更新服务器,这个方法会通过实体bean的细化的
    本地接口的set方法复制DTO的属性到相应的实体bean。客户端类似的可以通过本地填充一
    个域DTO并发送到工厂的createXXXEntity方法来创建实体bean。<图略>用前面的例子来讲,如果应用程序管理员希望更新一个特定的car或者manufacturer的话,
    这些更新可以通过不同的UI显示来完成(一个针对car,一个针对manufacturer)。这些更
    新会被处理,然后一个Car或者Manufacturer的域DTO会被发送回服务器端,在一次事务中
    进行更新,如图1.13所示。为了完成上述更新或者超出了CRUD范围的对域对象的更新,我们都应该通过传递自定义的DTO
    到会话/消息面板来更新服务器。记住,面板(外观)应该包含了应用程序中所有执行用例
    所需的商务逻辑,如在Amazon上下订单,或者在银行转账。对于这些操作而言,客户端通
    常是创建一个自定义的DTO,包含所有执行更新所需的数据,传递该DTO到面板,面板会相
    应的创建、更新、或者删除任意数量的服务器端资源。使用数据传输对象工厂的优势有很多:[更好的可维护性] 将应用程序逻辑(用例)同数据对象模型(实体bean)分隔开来,以便
    二者独立实现或改进。当客户端需求发生改变时,实体bean不再需要修改和重新编译。[鼓励实体bean重用] 实体bean可以跨项目重用,因为我们可以编写不同的DTO工厂来满足
    不同的应用程序需要。<图略>[允许创建DTO的复杂图表] 通过编写DTO创建逻辑,开发人员可以创建复杂的DTO图表/层次
    结构用于从复杂的实体bean层次结构中传递数据,这样的层次结构可以包括单对单、单对多、
    多对多、循环的、或者上述关系的组合。这使得客户端可以更细化的控制实体bean的哪些部
    分需要被用于显示。对于非基于Web的客户端如Java应用程序和applet,获取非表格类数据
    的能力是尤其重要的。[提高性能] 当DTO工厂模式被用作会话面板时,由多个实体bean取得的属性可以在单次网络
    调用中传递给客户端。数据传输对象工厂模式可以构建可维护的和灵活的系统,能提供一个简单的、持续统一的方
    法在单次网络调用中创建任意复杂的数据传输对象并传递给客户端,并且不会造成数据传输
    对象和J2EE系统中其他组件之间的相互依赖。[相关模式]会话面板模式
    数据传输对象模式
    值对象装配器(模式) (Alur, et al., 2001)