Skip to main content

模板方法模式

有时设计一个系统时已经直到了算法所需的关键步骤,而且确定了关键步骤的执行顺序,但步骤的具体实现未知。

例如银行办理业务的流程:取号、排队、办理具体业务。取号和排队对于每个顾客是一样的,办理具体业务因人而异。

角色:

  • 抽象类(Abstract Class):负责给出算法的轮廓和股价,由一个模板方法和若干基本方法构成
    • 模板方法:定义算法骨架,按某种顺序调用其包含的基本方法
    • 基本方法:实现算法的各个步骤的方法,是模板方法的组成部分:
      • 抽象方法(Abstract Method):由抽象类声明,子类实现
      • 具体方法(Concrete Method):在抽象类或具体类中声明并实现
      • 钩子方法(Hook Method):抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的同方法,一般返回值为 boolean

以炒菜为例

抽象类,定义模板方法和基本方法

/*
* 抽象类
* 定义模板方法和基本方法
*/
public abstract class AbstractClass {

//模板方法
public final void cookProcess(){
pourOil();
addVegetable();
addSauce();
fry();
}

//抽象方法和具体方法
public void pourOil(){
System.out.println("倒油");
}

public abstract void addVegetable();

public abstract void addSauce();

public void fry(){
System.out.println("炒菜");
}
}

两种实现:

public class ConcreteClass_Carrot extends AbstractClass {
public void addVegetable() {
System.out.println("加入萝卜");
}

public void addSauce() {
System.out.println("加盐加味精");
}
}
public class ConcreteClass_Tomato extends AbstractClass {
public void addVegetable() {
System.out.println("加入番茄");
}

public void addSauce() {
System.out.println("加入盐");
}
}

测试类

public class Client {
public static void main(String[] args) {
ConcreteClass_Carrot carrot=new ConcreteClass_Carrot();
carrot.cookProcess();
ConcreteClass_Tomato tomato=new ConcreteClass_Tomato();
tomato.cookProcess();
}
}

优缺点

优点:

  • 提高代码复用性,相同代码放在父类中
  • 实现反向控制,通过父类调用子类的操作,通过子类具体实现扩展不同行为

缺点:

  • 对每一个实现需要重新定义子类,导致类的个数增加
  • 父类抽象方法由子类实现,子类执行结果影响父类的结果,导致阅读代码难度增加

使用场景:

  • 算法整体步骤固定,个别步骤易变。
  • 需要通过子类决定父类某个方法是否执行,实现子类对父类反向控制(通过钩子函数)

JDK 中的模板方法模式

public abstract class InputStream implements closeable{

//抽象方法,子类需重写
public abstract int read() throws IOException;

public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}

int c = read(); //read 方法由子类实现
if (c == -1) {
return -1;
}
b[off] = (byte)c;

int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
}