OO Design Recommendations for J2EE Applications(2)
The Template Method Design Pattern
模板方法
One good use of concrete inheritance is to implement the Template Method design pattern.
类继承的一个好的用法是实现模板方法设计模式
The Template Method design pattern (GoF) addresses a common problem: we know the steps of an algorithm and the order in which they should be performed, but don't know how to perform all of the steps. This Template Method pattern solution is to encapsulate the individual steps we don't know how to perform as abstract methods, and provide an abstract superclass that invokes them in the correct order. Concrete subclasses of this abstract superclass implement the abstract methods that perform the individual steps. The key concept is that it is the abstract base class that controls the workflow. Public superclass methods are usually final: the abstract methods deferred to subclasses are protected. This helps to reduce the likelihood of bugs: all subclasses are required to do, is fulfill a clear contract.
模板方法设计模式(gof)强调了一个普遍的问题:我们知道算法的每一步,并且知道他们执行的顺序,但是我们不知道怎么样去执行所有的这些步骤。模板方法模式被用于去解决将我们不知道如何去执行的步骤做为一个抽象的方法,提供一个抽象的父类用正确的顺序调用他们。抽象父类的具体子类实现这个特定的方法。关键点是抽象类控制这个流程。公用父类方法通常是final的:抽象方法延至子类的是protected的。这帮助减少可能性的bug数:所有的子类被要求去做,满足了一个清晰的职责。
The centralization of workflow logic into the abstract superclass is an example of inversion of control. Unlike in traditional class libraries, where user code invokes library code, in this approach framework code in the superclass invokes user code. It's also known as the Hollywood principle: "Don't call me, I'll call you". Inversion of control is fundamental to frameworks, which tend to use the Template Method pattern heavily (we'll discuss frameworks later).
使工作流的逻辑集中到抽象类是控制反转的例子。不像传统的类库,在用户代码调用类库,通过这种方法在超类中的框架代码调用用户代码。它就是人尽皆知的好莱坞原则:“不要调用我,我将调用你”。控制反转是框架的基础,这使得模板方法模式更笨重。
For example, consider a simple order processing system. The business involves calculating the purchase price, based on the price of individual items, checking whether the customer is allowed to spend this amount, and applying any discount if necessary. Some persistent storage such as an RDBMS must be updated to reflect a successful purchase, and queried to obtain price information. However, it's desirable to separate this from the steps of the business logic.
例如,考虑考虑一个简单的订单处理系统。这个业务涉及计算购买价,基于每个物品的价格,检查是否顾客被允许花费这个数量,如果必要的时候打折扣。
一些持久存储例如rdbms必须去更新一个成功的购买记录入库,查询获得价格信息。然而,那是被期望去分离这些从业务逻辑的步骤中。
The AbstractOrderEJB superclass implements the business logic, which includes checking that the customer isn't trying to exceed their spending limit, and applying a discount to large orders. The public placeOrder() method is final, so that this workflow can't be modified (or corrupted) by subclasses:
AbstractOrderEJB父类实现了业务逻辑,包含了检查顾客是否超过了他们的购买限制,应用一个折扣对大订单。公共方法placeOrder()是final的,以至于工作流能被子类修改
public final Invoice placeOrder (int customerId, InvoiceItem[] items)
throws NoSuchCustomerException, SpendingLimitViolation {
int total = 0;
for (int i = 0; i < items. length; i++) {
total += getItemPrice (items [i]) * items [i] .getQuantity();
}
if (total > getSpendingLimit (customerId) ){
getSessionContext() .setRollbackOnly();
throw new SpendingLimitViolation (total, limit);
}
else if (total > DISCOUNT_THRESHOLD) {
// Apply discount to total...
}
int invoiceId = placeOrder (customerId, total, items);
return new InvoiceImpl (iid, total);
}
I've highlighted the three lines of code in this method that invoke protected abstract "template methods" that must be implemented by subclasses. These will be defined in AbstractOrderEJB as follows:
我高亮了在这个方法中的3行代码,他们调用了protected抽象“模板方法”,这些必须被子类实现,他们被定义在AbstractOderEJB中:
protected abstract int getItemPrice(InvoiceItem item);
protected abstract int getSpendingLimit(customerId)
throws NoSuchCustomerException;
protected abstract int placeOrder(int customerId, int total,
InvoiceItem[] items);
Subclasses of AbstractOrderEJB merely need to implement these three methods. They don't need to concern themselves with business logic. For example, one subclass might implement these three methods using JDBC, while another might implement them using SQLJ or JDO.
AbstarctOrderEJB的子类仅仅需要去实现这3个方法,他们不需要考虑他们自己的业务逻辑。例如,一个子类可能实现这3个方法用jdbc,然而其他的可能实现他们用sqlj或者jdo。
Such uses of the Template Method pattern offer good separation of concerns. Here, the superclass concentrates on business logic; the subclasses concentrate on implementing primitive operations (for example, using a low-level API such as JDBC). As the template methods are protected, rather than public, callers are spared the details of the class's implementation.
模板方法模式的这种用法提供了关注点的好的分离。这样,父类只需要集中与业务逻辑;子类集中于实现基本操作(例如,用用低级api像jdbc)。由于模板方法模式是受保护的,而不是公共方法,调用者被分离从类实现的细节中。
As it's usually better to define types in interfaces rather than classes, the Template Method pattern is often used as a strategy to implement an interface.
由于通常定义接口比定义类更好,模板方法模式通常被用于实现接口的策略。
Note
注意
Abstract superclasses are also often used to implement some, but not all, methods of an interface. The remaining methods – which vary between concrete implementations – are left unimplemented. This differs from the Template Method pattern in that the abstract superclass doesn't handle workflow.
抽象类也通常被用于去实现一些接口方法。剩下的方法--在不用的具体实现中是变化的--被留下未实现。模板方法模式和抽象类的不同在与处理工作流程。
Important
重要
Use the Template Method design pattern to capture an algorithm in an abstract superclass, but defer the implementation of individual steps to subclasses. This has the potential to head off bugs, by getting tricky operations right once and simplifying user code. When implementing the Template Method pattern, the abstract superclass must factor out those methods that may change between subclasses and ensure that the method signatures enable sufficient flexibility in implementation.
在抽象类中用模板方法模式定义一系列的算法,但是推迟不同的步骤实现到子类中。这潜在的阻碍了bug,抓住了复杂的操作简化了用户代码。当实现模板方法模式的时候,抽象父类必须提取这些可能在子类中改变的方法并且确保方法签名在实现时有足够的灵活性。
Always make the abstract parent class implement an interface. The Template Method design pattern is especially valuable in framework design (discussed towards the end of this chapter).
总是使抽象父类实现一个接口。模板方法设计模式在框架设计中特别有价值(在这章的结尾讨论了)。
The Template Method design pattern can be very useful in J2EE applications to help us to achieve as much portability as possible between application servers and databases while still leveraging proprietary features. We've seen how we can sometimes separate business logic from database operations above. We could equally use this pattern to enable efficient support for specific databases. For example, we could have an OracleOrderEJB and a DB2OrderEJB that implemented the abstract template methods efficiently in the respective databases, while business logic remains free of proprietary code.
模板方法模式在j2ee应用中非常游泳的帮助我们去完成更多的可移植性在应用服务器和数据库间,同时还保持了专有的特性。我们已经看到我们有时从数据库上分离业务逻辑。我们能同样的用这个模式去有效的支持特定的数据库。例如,我们有OracleOrderEJB和DB2OrderEJB在各自的数据中有效的实现了抽象模板方法,虽然业务逻辑仍然自由的对所有的代码。
The Strategy Design Pattern
策略模式
An alternative to the Template Method is the Strategy design pattern, which factors the variant behavior into an interface. Thus, the class that knows the algorithm is not an abstract base class, but a concrete class that uses a helper that implements an interface defining the individual steps. The Strategy design pattern takes a little more work to implement than the Template Method pattern, but it is more flexible. The advantage of the Strategy pattern is that it need not involve concrete inheritance. The class that implements the individual steps is not forced to inherit from an abstract template superclass.
模板方法模式的另一个选择是策略模式,它抽取了多变的行为到一个接口中。因此,了解算法的类不是一个抽象类,而是一个定义了具体的操作步骤,用帮助类实现了接口的具体的类。策略设计模式比模板方法模式花费了更多的工作去实现,但是它也更灵活。策略模式的优点是它不需要调用具体的类。实现类的具体操作步骤没有被强迫去从一个抽象模板类中继承。
Let's look at how we could use the Strategy design pattern in the above example. The first step is to move the template methods into an interface, which will look like this:
让我们看我们如何用策略模式在下面的例子中。第一步将模板方法移到一个接口中,这看起来想这样:
public interface DataHelper {
int getItemPrice (InvoiceItem item);
int getSpendingLimit (customerId) throws NoSuchCustomerException;
int placeOrder (int customerId, int total, InvoiceItem[] items);
}
Implementations of this interface don't need to subclass any particular class; we have the maximum possible freedom.
实现这个接口不需要任何特定的子类;我们有最大限度的自由。
Now we can write a concrete OrderEJB class that depends on an instance variable of this interface. We must also provide a means of setting this helper, either in the constructor or through a bean property. In the present example I've opted for a bean property:
现在我写一个依靠这个接口的实例变量的具体OrderEJB。我们必须提供一种手段设置这个帮助类,或者在构造方法或者通过一个bean属性。在当前的例子中我选择bean属性。
private DataHelper dataHelper;
public void setDataHelper (DataHelper newDataHelper) {
this.dataHelper = newDataHelper;
}
The implementation of the placeOrder() method is almost identical to the version using the Template Method pattern, except that it invokes the operations it doesn't know how to do on the instance of the helper interface, in the highlighted lines:
placeOrder()方法的实现几乎与用模板方法模式是相同的,除了它调用的这个操作它不知道如何实例化这个helper接口,在高亮行中:
public final Invoice placeOrder (int customerId, InvoiceItem[] items)
throws NoSuchCustomerException, SpendingLimitViolation {
int total = 0;
for (int i = 0; i < items.length; i++) {
total += this.dataHelper.getItemPrice(items[i]) *
items[i].getQuantity();
}
if (total > this.dataHelper.getSpendingLimit(customerId)) {
getSessionContext() .setRollbackOnly();
throw new SpendingLimitViolation(total, limit);
} else if (total > DISCOUNT_THRESHOLD) {
// Apply discount to total...
}
int invoiceId = this.dataHelper.placeOrder (customerId, total, items);
return new InvoiceImpl (iid, total);
}
This is slightly more complex to implement than the version using concrete inheritance with the Template Method pattern, but is more flexible. This is a classic example of the tradeoff between concrete inheritance and delegation to an interface.
这稍微的更复杂去实现比用模板方法模式在类继承的版本中,但是也更灵活。这个一个经典的转换例子在类继承和委托到一个接口。
I use the Strategy pattern in preference to the Template Method pattern under the following circumstances:
在下面的情况下,我用策略模式更优于模板方法模式:
-
When all steps vary (rather than just a few).
-
当所有的步骤是不同的(比起只有一点点)
-
When the class that implements the steps needs an independent inheritance hierarchy.
-
当类的实现步骤需要一个依赖继承层次。
-
When the implementation of the steps may be relevant to other classes (this is often the case with J2EE data access).
-
当步骤的实现可能关联了其他的类(这在j2ee数据访问中经常发送)。
-
When the implementation of the steps may need to vary at run time. Concrete inheritance can't accommodate this; delegation can.
-
当这个实现的步骤需要在运行时变化。类继承不能适应这个,代理能。
-
When there are many different implementations of the steps, or when it's expected that the number of implementations will continue to increase. In this case, the greater flexibility of the Strategy pattern will almost certainly prove beneficial, as it allows maximum freedom to the implementations.
-
当这里有许多不同的实现步骤,或者当它期待这个实现的数量将继续增长。在这种情况下,这个策略模式极大的灵活性将几乎必定提供益处,因为它允许自由实现的最大化。