定义
模板方法模式(Template Method)
定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成。
模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。
因此,在模板方法模式的类结构图中,只有继承关系,没有关联关系。
模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。
代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。
// 范例
abstract class Template {
public abstract void OperationA();
public abstract void OperationB();
public void TemplateMethod() {
OperationB();
OperationA();
}
}
class ConcreteA extends Template {
@Override
public void OperationA() {
System.out.println("ConcreteA OperationA()");
}
@Override
public void OperationB() {
System.out.println("ConcreteA OperationB()");
}
}
class ConcreteB extends Template {
@Override
public void OperationA() {
System.out.println("ConcreteB OperationA()");
}
@Override
public void OperationB() {
System.out.println("ConcreteB OperationB()");
}
}
public class TemplateMethodPattern {
public static void main(String[] args) {
Template obj = null;
obj = new ConcreteA();
obj.TemplateMethod();
obj = new ConcreteB();
obj.TemplateMethod();
}
}
// 运行结果
ConcreteA OperationB()
ConcreteA OperationA()
ConcreteB OperationB()
ConcreteB OperationA()
要点
模板方法模式中的三类角色
1、具体方法(Concrete Method)
2、抽象方法(Abstract Method)
3、钩子方法(Hook Method)
三类角色的关联
在模板方法模式中,首先父类会定义一个算法的框架,即实现算法所必须的所有方法。
其中,具有共性的代码放在父类的具体方法中。
各个子类特殊性的代码放在子类的具体方法中。但是父类中需要有对应抽象方法声明。
钩子方法可以让子类决定是否对算法的不同点进行挂钩。
总结
使用模板方法模式可以将代码的公共行为提取,以达到复用的目的。
而对于特殊化的行为在子类中实现。父类的模板方法可以控制子类中的具体实现。
子类无需了解整体算法框架,只需实现自己的业务逻辑即可。
实例
模板方法模式应用场景十分广泛。
在《Head First》的模板方法模式章节里列举了一个十分具有代表性的例子。
现实生活中,茶和咖啡是随处可见的饮料。冲泡一杯茶或冲泡一杯咖啡的过程是怎样的?
我们来整理一下流程。
泡茶: |
由以上处理步骤不难发现,准备这两种饮料的处理过程非常相似。我们可以使用模板类方法去限定制作饮料的算法框架。
其中相同的具有共性的步骤(如烧开水、倒入杯中),直接在抽象类中给出具体实现。
而对于有差异性的步骤,则在各自的具体类中给出实现。
//抽象类
abstract class Beverage {
// 模板方法,决定了算法骨架。相当于TemplateMethod()方法
public void prepareBeverage() {
boilWater();
brew();
pourInCup();
if (customWantsCondiments())
{
addCondiments();
}
}
// 共性操作,直接在抽象类中定义
public void boilWater() {
System.out.println("烧开水");
}
// 共性操作,直接在抽象类中定义
public void pourInCup() {
System.out.println("倒入杯中");
}
// 钩子方法,决定某些算法步骤是否挂钩在算法中
public boolean customWantsCondiments() {
return true;
}
// 特殊操作,在子类中具体实现
public abstract void brew();
// 特殊操作,在子类中具体实现
public abstract void addCondiments();
}
// 具体类
class Tea extends Beverage {
@Override
public void brew() {
System.out.println("冲泡茶叶");
}
@Override
public void addCondiments() {
System.out.println("添加柠檬");
}
}
class Coffee extends Beverage {
@Override
public void brew() {
System.out.println("冲泡咖啡豆");
}
@Override
public void addCondiments() {
System.out.println("添加糖和牛奶");
}
}
// 测试代码
public static void main(String[] args) {
System.out.println("============= 准备茶 =============");
Beverage tea = new Tea();
tea.prepareBeverage();
System.out.println("============= 准备咖啡 =============");
Beverage coffee = new Coffee();
coffee.prepareBeverage();
}
// 运行结果
============= 准备茶 =============
烧开水
冲泡茶叶
倒入杯中
添加柠檬
============= 准备咖啡 =============
烧开水
冲泡咖啡豆
倒入杯中
添加糖和牛奶