软讯网络 > 编程语言 > Java > Session Facade 的规则和模式(2)
【标 题】:Session Facade 的规则和模式(2)
【关键字】:
c,模式,io,Session,on,Session,Facade
【来 源】:网络
Session Facade 的规则和模式(2)
Session Facade 的规则和模式(2)
Session Facade 的重要规则
那么我们该如何应用这些关于针对会话的 Facade 的规则呢?这对我们的 EJB 设计又意味着什么呢?我在设计Session Facade 时遵循三条基本原则:
它们自己不做实际工作;它们委派其它对象做实际工作。这意味着Session facade 中的每个方法都应该很小(异常处理逻辑不计算在内,代码应为五行或更少)。
它们提供简单的接口。这意味着 facade 方法的数量应相对较少(每个Session bean 中仅有约 24 个)。
它们是底层系统的客户端接口。它们应该把特定于子系统的信息封装起来,并且不应该在不必要的情况下公开它。
那么它的工作机制呢?您还能代理别的哪些类型的对象呢?这又会给您的设计带来什么好处呢?在我的一篇早期论文和 [Brown 2001] 这本书中,我已论述了其中一些问题,在那里可以找到一些详细信息。但,总的来说,在我的多数 EJB 设计中我通常会找到以下四类对象:
值对象是包含了客户机所请求的数据的、可序列化的 Java bean。它包含Entity bean 和其他数据源所包含的数据的一个子集。它是Session EJB 方法的返回类型。[EJB 2.0] 和 [Sun 2001] 都描述了值对象和值对象的用途。请注意 [Fowler 2001] 称其为“数据传输对象”( Data Transfer Objects ),[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。
一个 EJB 对象示例
描述类似这样的模式遇到的一个问题是,能够使用这种模式的示例都太大,以至于无法包含在模式自身的描述中。尽管如此,我还是要尝试举出如下示例(它显然很简单)来说明一下这些对象看起来是什么样子。
假设我们正在为银行构建一个 ATM 系统。这是最老掉牙的 OO 设计问题之一,当然其它很多书籍和论文已经讨论过它,但它确实有足够符合我们要求的有趣特点。通过分析,我们发现了两种 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 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)
throws java.rmi.RemoteException,
com.ibm.bankexample.domain.FactoryException;
void transfer(java.lang.String fromAccount, java.lang.String toAccount, double amount)
throws java.rmi.RemoteException,
com.ibm.bankexample.domain.InsufficientFundsException,
com.ibm.bankexample.domain.FactoryException;
void withdraw(java.lang.String accountNumber, double amount)
throws java.rmi.RemoteException,
com.ibm.bankexample.domain.InsufficientFundsException,
com.ibm.bankexample.domain.FactoryException;
}
同样地,帐户 EJB 的远程接口如下:
package com.ibm.bankexample.ejbs;
/**
* This is the Enterprise Java Bean Remote Interface
* for the Account Entity EJB.
*/
public interface Account extends javax.ejb.EJBObject {
void deposit(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 setUserid(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 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 result;
}
这个方法展示了Session Facade EJB 的方法的标准模式。它找到合适的帮助对象(Action 或 Factory,在本例中是 Factory),调用帮助对象上的业务方法,然后返回结果。
如这个方法所指出的,我们需要一个 AccountFactory 类来从 Accounts 构建 AccountValues。这个类的类定义如下:
public class AccountFactory {
private static AccountHome accountHome = null;
}
AccountFactory 的 getAccounts(userid) 方法的实现如下:
public java.util.Vector getAccounts(String userid) throws FactoryException {
try {
Vector 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());
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) {
try {
java.lang.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 createTimeSheetHome()");
}
}
return accountHome;
}
正如 [Brown 2001] 和 [Gunther 2000] 所描述的那样,在 WebSphere® 中,高速缓存 EJB home 是一个极好的习惯,因为获取 JNDI InitialContext 和从 InitialContext 获取 EJB Home 需要一段时间。
既然您已经看到了Session、Entity和 Factory 如何组合在一起,那我们就来看一个 Action 类的示例。在本例中,我们有一个处理从一个帐户到另一个帐户的资金转移的 Transfer 对象。Transfer 由 ATM EJB 中的 transfer() 方法的实现中创建,该方法的实现如下:
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 Transfer {
private static AccountHome accountHome;
}
transfer() 方法的实现如下:
public void transfer(String fromAccount, String toAccount,
double amount) throws InsufficientFundsException, FactoryException {
try {
Account from = getAccountHome().findByPrimaryKey(
new AccountKey(fromAccount));
Account to = getAccountHome().findByPrimaryKey(
new AccountKey(toAccount));
if (from.getBalance() < amount)
throw new InsufficientFundsException();
to.deposit(amount);
from.withdraw(amount);
} catch (Exception e) {
throw new FactoryException(
"cannot perform transfer. Nested exception is " + e);
}
}
您已经看到,Transfer 对象中的 transfer() 方法处理以下细节:定位两个 Account 实体,确保“From”帐户有足够的余额,把转移金额存入“To”帐户,从“From”帐户中提出转移金额。同样地,您可以看到 Action 对象的其它方法可以实现您系统中的其它业务规则。
(未完待续)
|