站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题设计模式 1

设计模式 1

摘要: 设计模式 1

第一个设计模式非常简单。一个公司和雇员的Entity Bean和 下面给出的Entity Bean的代码片断是类似的。它们是由jbuilder4的 EntityBean模版生成的。所有的字段都声明为public的cmp字段。
Code snippet for Company Entity Bean public class CompanyBean implements EntityBean { EntityContext entityContext; public Integer comId; //the primary key public String comName; //the company name public String comDescription //basic description public Timestamp mutationDate //explained later public Integer ejbCreate() throws CreateException { return null; } //various get() and set() for every column/field // which are exposed in the Remote Interface as well
Code snippet for Employee Entity Bean public class EmployeeBean implements EntityBean { EntityContext entityContext; public Integer empId; //the primary key public Integer comId; //the company foreign key public String empFirstName; //the employee firstname public String empLastName // the employee lastname public Timestamp mutationDate //explained later public Integer ejbCreate() throws CreateException { return null; } //various get() and set() for every column/field // which are exposed in the Remote Interface as well
这个设计模式虽然很简单,但是却有很多缺点,比如,对每一个 字段的访问都会导致对get()和set()方法的一次远程调用。而远 程过程调用(RPCs)是非常耗费资源的,并且,对于在实际中通 常要求的组合的访问会导致一系列的远程调用。可以说,这个模 式在实际中可用性很差。上面展示的设计模式可以作为其他设计 模式的基础,比如RAD,原型设计,测试等。这时,那个代表雇 员的Employee Entity Bean并没有展示出在雇员和公司之间有何 关系。 设计模式2 为了避免设计模式1的缺点,我们介绍一下封装 entity bean值域的value objec的概念。value object, 用某些语言的术语来说,就是一个结构类型,因为他们 和corba的结构类型非常类似。
Value Object code snippet for Company public class CompanyStruct implements java.io.Serializable { public Integer comId; //Primary Key public String comName; public String comDescription; public java.sql.Timestamp mutationDate; }
Value Object code snippet for Employee public class EmployeeStruct implements java.io.Serializable { public Integer empId; //Primary Key public Integer comId; //Foreign Key public String empFirstName; public String empLastName; public java.sql.Timestamp mutationDate; }
现在,公司和雇员的entity bean可以把上面的一个结构类型作为 ejbCreate()的一个参数。由于这个结构封装了entity的所有字段 的值,entity bean只需要一个getdata()和setdata()方法就可以 对所有的字段进行操作。
Code snippet for an Entity Bean’s create() public Integer ejbCreate(CompanyStruct struct) throws CreateException { this.comId = struct.comId; this.comName = struct.comName; this.comDescription = struct.comDescription; this.mutationDate = struct.mutationDate; return null; } Code snippet for an Entity Bean’s getData() public CompanyStruct getData() { CompanyStruct result = new CompanyStruct(); result.comId = this.comId; result.comName = this.comName; result.comDescription = this.comDescription; result.mutationDate = this.mutationDate; return result; } Code snippet for an Entity Bean’s setData() public void setData(CompanyStruct struct) { this.comName = struct.comName; this.comDescription = struct.comDescription; this.mutationDate = struct.mutationDate;; }
跟设计模式1中使用单独的get()和set()方法去操作特定字段不同, 在设计模式2中,我们避免这种情况而只需要进行一次远程调用就 可以了。现在,只有一个事务通过一次远程调用就操作了所有的数 据。这样,我们就避免了设计模式1的大部分缺点,除了建立bean 之间的关系外。 虽然setdata()方法可以对所有字段赋值,但是,borland appserver 提供了一种智能更新的特性,只有被修改过的字段才会被重新写入数 据库,如果没有字段被修改,那么ejbStore()方法将会被跳过。 borland程序员开发指南(EJB)有更详细的描述。 同样,在entity bean和struct之间存在这重复的代码,比如同 样的字段声明。这意味着任何数据库表结构的修改都会导致 entity beabn和struct的改变,这使得同步entity和struct变得 困难起来。 一个小小的改进可以从一定程度上避免这种情况, 就是在ebCreate()方法中调用setddata()方法,这可以消除一 些冗余的代码。
Code snippet for an Entity Bean’s create() public Integer ejbCreate(CompanyStruct struct) throws CreateException { this.comId = struct.comId; //set the primary key setData(struct);//this removes some redundant code return null; } 设计模式3 在设计模式2中我们看到,在entity bean和struct之间 有很多重复的代码比如同样的字段声明(对应数据库中的表列)。 如果让entity bean从结构继承下来就可以避免冗余的代码。但是 这种设计,仍然不能显示beans之间的联系。
Code snippet for Company Entity Bean public class CompanyBean extends CompanyStruct implements EntityBean { EntityContext entityContext; //all fields in CompanyStruct are available for CMP public Integer ejbCreate(CompanyStruct Struct) throws CreateException { this.comId = struct.comId; //set the primary key setData(struct);//this removes some redundant code return null; }
其余的代码比如getdata()和setdata()方法的实现和设计模式2中 是完全一样的
设计模式4 在设计模式3中我们看到使bean从struct继承后使得代码大 幅缩水并且所有的字段都可定义为cmp字段。这里,我们可 以更进一步修正setdata()和getdata()的实现方法来减少代码量。 我们为这个struct增加一个方法。
Value Object code snippet for Company public class CompanyStruct implements java.io.Serializable { public Integer comId; public String comName; public String comDescription; public Timestamp mutationDate; public void copyFrom(CompanyStruct struct) { comId = struct.comId; comName = struct.comName; comDescription = struct.comDescription; mutationDate = struct.mutationDate; } }
由于entity bean是从struct继承下来的,在bean的实现类 中也一样可以引用copyfrom()方法,当然,必须注意的是, 这个copyfrom()方法并不是一个商业方法,它不需要在bean 的远程接口中暴露给调用者。 现在,getdata()和setdata()方法可以简化更进一步的简化。
Code snippet for an Entity Bean’s getData() public CompanyStruct getData() { CompanyStruct result = new CompanyStruct(); result.copyFrom(this); return result; }
这里把this作为一个参数传入copyfrom()。由于enttity bean 从struct继承而来,于是这个entitty bean便可以作为一个 struct传入。 EJB容器并不赞成把this指针作为一个参数传递因为在两个控 制线程中同时访问一个bean的实例可能会引起事务冲突。但事 实上我们所做的并没有违背这个原则,因为我们的并没有在 bean之间传递this的引用并且也没有引用任何可能引起事务冲突的方法。
Code snippet for an Entity Bean’s setData() public void setData(CompanyStruct struct) { this.copyFrom(struct); } 类似的,上面的方法把struct的值赋给enttity bean。
对于一个映射到有很多列的表的entity bean,这种实现 方法的优点是使得bean实现类的代码非常简单。这种设 计模式使得代码及其精简,可读性和可维护性也大大增强。
任何数据库的修改都只需要修改作为基类的struct,而几 乎不需要修改bean的代码。把这种改变从struct分离出来, 当cmp字段发生改变时需要修改部署描述符。这就使得开 发时能够更好的适应设计的改变。 这里,还是没有实现bean之间的关系,这将在设计模式5中解决。
就像我们在设计模式4中看到的, Entity Bean的实现大小被缩减到在ejbCreate(), getData() and setData()方法中的仅仅几行,不管CMP字段的数目. 下一步是建模公司和雇员的Entity Beans,这个有点繁琐而且建议读者先对borland 公司的的OR Mapping和高级CMP有所了解. 对这个关系建模根本不需要对结构的代码变化,然而Entity Beans实现类需要一点点 修改来反映两个实体间的关系,鉴于此Deployment Descriptor需要有小的修改. 象以前, Entity Bean从结构继承,下面是公司Entity Bean的代码片段: public class CompanyBean extends CompanyStruct implements EntityBean { EntityContext entityContext; // CMP for all fields in the CompanyStruct public java.util.Collection employees; //one-to-many //rest of the code including getData() and setData() public java.util.Collection getEmployees() { return employees; } } 下面是雇员Entity Bean的程序片段: public class EmployeeBean extends EmployeeStruct implements EntityBean { EntityContext entityContext; //CMP for all fields in EmployeeStruct EXCEPT //the comId public Company company;//remote reference to company } 在上面的程序片段中,雇员Entity Bean从雇员结构继承,雇员结构本身有 一个字段comId表示雇员和公司之间的的外键,在所有的前面的设计模式中, 这个字段是CMP的.而在设计模式5中这个字段用在Deployment Descriptor中 un-checking的方法从CMP中去掉.而对公司Entity Bean的远程引用现在是CMP的. 现在的问题是怎么在getData()和SetData()方法中更新公司Entity Bean的引用, 当这些方法只get和set comId(在设计模式上下文中没有被CMP)的值. 简单的说,过程的结构没有变化并且字段comId(不再CMP)在RPC中被拷贝到 Entity Bean和从Entity Bean拷贝出来.需要的是对公司Entity Bean的远程 引用在必须被写入数据库和从数据库读出时更新.我们需要用ejbLoad()和ejbStore() 方法在Entity Bean实现类中为我们完成这项工作. 在雇员Entity Bean中的ejbLoad()方法的代码片段如下: public void ejbLoad() { try { comId=(company == null)?null:(Integer)company.getPrimaryKey(); } catch (Exception e) { //throw some runtime exception (e.g. EJBException) } } 以上代码几乎不需要解释.当数据被从数据库中读出(在事务的开始时候), comId(不是CMP)字段在雇员Entity Bean被set.因此当getData()方法被调用时, 返回的结构将包含正确地comId的值. 在雇员Entity Bean中的ejbStore()方法如下: public void ejbStore() { try { company = (comId == null)?null:beanGlossary.getCompanyHome().findByPrimary Key(comId); } catch (Exception e) { //throw some runtime exception (e.g. EJBException) } } ejbStore()在事务结束当数据被写入数据库时被调用.在这种情况下,comId的值 被修改(通过调用setData方法),this必须被写到数据库中.在上面方法中的代码 把comId转化成公司的远程引用.(毕竟comId是公司Entity Bean的主键). 使用空check的原因是数据库不能存空值(表之间的弱引用),并且这些同样需要建模. 任何情况下,用java对基本类型的封装要比使用基本类型自己好,因为他们能 存空值而且易于转换成其他形式. 上面的BeanGlossary类的代码片断容易引起一些混淆. 这实际上是一个捕获EJB的lookup的utility类(一个无状态session bean), 在entity bean和有状态session bean的情况下,Home接口的lookup是被缓冲的. 在无状态session bean的情况下,Remote接口是被缓冲的(作为ejb规范1.1的一部分, 一个SLSB在Home接口中调用的create()是不被优化的). 通过在上面上下文的缓冲,我们意思是第一个请求是被lookup的.随后的调用是得到 已经在对象引用中初始化的home接口或remote接口. BeanGlossarySB utility类的代码片段如下: public class BeanGlossarySB implements SessionBean { private Context context = null; public javax.naming.Context getContext() throws NamingException { if (context == null) context = new javax.naming.InitialContext(); return context; } // Company private CompanyHome companyHome = null; public CompanyHome getCompanyHome() throws NamingException { if (companyHome == null) companyHome = ((CompanyHome) javax.rmi.PortableRemoteObject.narrow( getContext().lookup("java:comp/env/ejb/Company"), CompanyHome.class)); return companyHome; } // rest of the EJBs } 在设计模式5中,我们没有处理Entity Bean的Home接口. 在雇员Entity Bean的情况下, 会有一个finder元素在 findEmployeesByCompany(Company pCompany)的几行中, 这将会返回雇员远程引用的集合. 在公司Entity Bean 中的Deployment Descriptor map了在上面定义的finder元素的雇员集合. 这样,在公司Entity Bean中的方法getEmployees()在remote接口中的调用 返回需要的与那家公司相联系的远程引用的雇员的集合.
↑返回目录
前一篇: EJB实质问题
后一篇: EJB设计模式1