模板方法模式是一种行为型设计模式。其思想是:将部分逻辑以具体方法的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑,不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
模板方法模式涉及到2个角色:
- 抽象模板(Abstract Template):
- 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
- 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
- 具体模板(Concrete Template):
- 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
结构图:
具体代码实现:
public abstract class AbstractTemplate {
protected abstract void abstractMethod1();
protected abstract void abstractMethod2();
// 模板方法
public final void templateMethod() {
abstractMethod1();
abstractMethod2();
}
}
public class ConcreteTemplateA extends AbstractTemplate {
@Override
public void abstractMethod1() {
System.out.println("method 1 A");
}
@Override
public void abstractMethod2() {
System.out.println("method 2 A");
}
}
public class ConcreteTemplateB extends AbstractTemplate {
@Override
public void abstractMethod1() {
System.out.println("method 1 B");
}
@Override
public void abstractMethod2() {
System.out.println("method 2 B");
}
}
// 测试
class TemplateTest {
public static void main(String[] args) {
AbstractTemplate template = new ConcreteTemplateA();
template.templateMethod();
}
}
运行结果:
method 1 A
method 2 A
另外,模板中除了模板方法和抽象方法外,还有一种称为钩子的方法。钩子方法常常由抽象类给出一个空实现作为此方法的默认实现。这种空的钩子方法叫做“Do Nothing Hook”。显然,这种默认钩子方法与缺省适配模式的思路是一样的,这样具体模板类就不必实现所有的钩子,因为有些钩子其实是用不上的。钩子方法的命名应当以do
开头,例如,在HttpServlet
类中,doGet()
、doPost()
等就是这样命名的。
这里,我们使用模板方法模式将策略模式帖子里例子再实现一遍。
// 抽象账单
public abstract class AbstractBill {
protected double getTotal() {
return 1000; // 模拟一个原始总价
}
protected abstract double cost(); // 此方法由具体模板实现
// 模板方法:计算最后总价
public final double finalBill() {
return cost();
}
}
// 非会员账单
public class CommonBill extends AbstractBill {
@Override
public double cost() {
return getTotal(); // 原价
}
}
// 会员账单
public class VipBill extends AbstractBill {
@Override
public double cost() {
return getTotal()*0.88; // 88折
}
}
public class Counter {
private AbstractBill bill;
public Counter(AbstractBill bill) {
this.bill = bill;
}
public void showBill() {
System.out.printf("the final bill costs %f\n", bill.finalBill());
}
}
// 测试
class CounterTest {
public static void main(String[] args) {
// 模拟会员结账
Counter counter = new Counter(new VipBill());
counter.showBill();
}
}
运行结果:
the final bill costs 880.000000
结构图:
可以看到,在策略模式
的例子里,如果我们把抽象策略接口和提供公共方法抽象类二合为一,得到的代码结构与本例十分相似。但是,那并不是模板方法模式,因为当中并没有提供模板方法。
更多关于策略模式与模板方法模式的区别,可以参考这篇博文:
《模板方法模式、策略模式 的 联系、区别和应用场景》 - 赛艇队长 - 博客园