设计模式-模版模式

  1. 意图
  2. 例子
  3. 适用性
  4. 结构
  5. 协作
  6. 效果

意图

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

例子

准备算法

public abstract class Tea {
final public void prepare() {
water();
eat();
}

void water();
void eat();
}

final表示无法重写,因此该方法只能使用,无法覆盖,该顺序已经被固定成一个模板。
public class FlowerTea extends Tea {
public void water() {
// TODO
}
public void eat() {
// TODO
}
}

适用性

  1. 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  3. 控制子类扩展。模板方法只在特定点调用“许在这些点进行扩展。hook”操作, 这样就只允许在这些点进行扩展。

结构

github

协作

ConcreteClass 靠 AbstractClass 来实现算法中不变的步骤。

效果

模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。 模板方法导致一种反向的控制结构,这种结构有时被称为“好莱坞法则”,即“别找我们,我们找你” [ S w e 8 5 ] 。 这指的是一个父类调用一个子类的操作,而不是相反 。 模板方法调用下列类型的操作:

  • 具体的操作(ConcreteClass或对客户类的操作)。
  • 具体的AbstractClass的操作(即,通常对子类有用的操作)。
  • 原语操作(即,抽象操作)。
  • FactoryMethod。
  • 钩子操作(hook operations),它提供了缺省的行为,子类可以在必要时进行扩展。一个钩子操作在缺省操作通常是一个空操作。

很重要的一点是模板方法应该指明哪些操作是钩子操作(可以被重定义)以及哪些是抽象操作(必须被重定义)。要有效地重用一个抽象类,子类编写者必须明确了解哪些操作是设计为有待重定义的。
子类可以通过重定义父类的操作来扩展该操作的行为,其间可显式地调用父类操作。

DerivedClass::Operation() {
ParentClass::Operation();
}

不幸的是,人们很容易忘记去调用被继承的行为。我们可以将这样一个操作转换为一个模板方法,以使得父类可以对子类的扩展方式进行控制。也就是,在父类的模板方法中调用钩子操作。子类可以重定义这个钩子操作:
ParentClass::Operation() {
HookOperation();
}

ParentClass本身的HookOperation什么也不做 :
ParentClass::HookOperation() {}

子类重定义HookOperation以扩展它的行为:
DerivedClass::HookOperation {

}