模板方法模式

类的行为型模式

模式动机

模板方法模式是基于继承的代码复用技术,在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中

模板方法模式中需要一个抽象类,将部分逻辑以具体方法的形式实现再声明一些抽象方法让子类实现剩余的不同逻辑。不同子类自然可以以不同的方式实现这些抽象方法,从而使剩余的逻辑有不同的具体实现

应用

模板方法模式定义一个算法的骨架,再将一些步骤的具体实现延迟到子类中,使得子类可以不改变一个算法的结构就重定义算法的具体实现。

模板方法模式包含如下角色:

  • 抽象类AbstractClass
  • 具体子类ConcreteClass

分析

模板方法模式中只有类之间的继承关系,没有对象关联关系;在设计时需要给出算法的骨架和算法的各个具体逻辑步骤。实现具体逻辑步骤的方法称为基本方法,而将基本方法汇总起来的方法称为模板方法

一个模板方法是定义在抽象类中、把基本操作方法组合在一起形成的一个总算法的方法。

基本方法是算法实现的各个基本步骤(单步,比如第一步第二步),是模板方法的组成部分:

  • 抽象方法
  • 具体方法
  • 钩子方法:父类预留的空方法或默认方法,允许子类进行覆盖

钩子方法示例:

public void template() {
open();
display();
if(isPrint()) {
print();
}
}
// 钩子方法isPrint
public boolean isPrint() {
return true;
} // 子类的isPrint可以根据实际情况返回true/false

典型抽象类代码如下:

public abstract class AbstractClass {
public void templateMethod () { // 模板方法
primitiveOperation1();
primitiveOperation2();
primitiveOperation3();
}
public void primitiveOperation1() { // 基本方法——具体方法
// 实现代码
}
public abstract void primitiveOperation2(); // 基本方法——抽象方法
public void primitiveOperation3() // 基本方法——钩子方法
{
// 空方法作为默认实现
}
}

典型具体子类代码如下:

public class ConcreteClass extends AbstractClass {
public void primitiveOperation2() {
// 实现代码
}
public void primitiveOperation3() {
// 实现代码
}
}

具体子类的基本方法将覆盖父类中定义的基本方法,子类的钩子方法也将覆盖父类的钩子方法 ,从而可以通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制,这是通过面向对象的多态实现的。

实例:银行业务办理

在银行办理业务时,一般都包含几个基本步骤,首先需要取号排队,然后办理具体业务,最后需要对银行工作人员进行评分。无论具体业务是取款、存款还是转账,其基本流程都一样。

优点

  1. 可以在一个类中抽象地定义算法,让子类去实现细节的处理
  2. 代码复用(比如默认方法如果不需要改就可以直接复用)
  3. 控制反转,父类调用的实际是子类实现的方法,所以子类可以控制父类的行为;并且通过子类扩展增加新的行为也不需要修改其他具体类,符合开闭原则
  4. 符合单一职责原则(每个具体子类只实现一个步骤),提高类的内聚性

缺点

每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统变复杂,设计也更抽象

适用场合

  • 一次性实现一个算法不变的部分,并将可变的行为留给子类实现
  • 各子类中的公共行为应当被提取出来并集中在一个公共父类中以避免重复代码
  • 分割一些复杂算法,将算法中固定不变的部分设计为模板方法和父类中的默认具体方法,将其他可以改变的细节交给子类
  • 控制子类扩展

模板方法模式广泛应用于框架设计,比如Spring等,主要是为了确保父类控制处理流程的逻辑顺序(比如初始化阶段)

public void runBare() throws Throwable {
setUp();
try {
runTest();
}
finally {
tearDown();
}
}

模式扩展

继承

模板方法模式鼓励恰当使用继承,将可复用的一般性的行为代码移到父类里,而将特殊化的行为代码移到子类里。继承复用本身的确存在一些问题(见面向对象设计原则),但模板方法模式确实体现了继承的优势,给开发人员带来了方便。

好莱坞原则

模板方法模式中,子类不显式调用父类方法,而是通过覆盖父类方法实现具体的业务逻辑,父类控制对子类的调用。这种机制叫做好莱坞原则(Don’t call us, we’ll call you.),由父类来控制整个过程,子类只负责具体实现。

钩子方法的使用

钩子方法使得子类可以控制父类的行为。

最简单的钩子方法就是空方法,也可以定义一个默认实现,如果子类不覆盖钩子方法,那就执行父类的默认实现(不能是抽象方法,不然子类必须实现)

复杂一点的钩子可以对其它方法进行约束,这种钩子通常返回布尔值,用来判定某个步骤是否执行,常用于子类根据自身的实际情况(某些属性、类本身特性等因素)决定返回true/false

(•‿•)