定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
是不是干巴巴的,通俗点理解就是:流程是固定的,但是流程里的行为可以定义。
大家都举的栗子,我也举一下:
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
模板方法结构如下:
1、抽象类/抽象模板(Abstract Class)
负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成:
(1)模板方法: 定义算法的骨架,按照某种顺序调用其包含的基本方法。
(2)基本方法: 实现算法各个步骤的方法,是模板方法的组成部分,可以分为三种:
(a)抽象方法:在抽象类中声明,由具体子类实现。
(b)具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
(c)钩子方法:在抽象类中已经实现,包含用于判断的逻辑方法( isXXX():boolean)和需要子类重写的空方法
对于抽象方法或者接口中定义的方法的一个空实现,在实际中的应用,比如说有一个接口,而你只想用其中一个方法,那么可以写一个抽象类实现这个接口,在这个抽象类里将你要用的那个方法设置为abstract,其它方法进行空实现,然后你再继承这个抽象类,就不需要实现其它不用的方法,这就是钩子方法的作用。
2、具体子类/具体实现(Concrete Class)
实现抽象类中所定义的抽象方法和钩子方法,他们是一个顶级逻辑的组成步骤。
我们用食堂打饭来举例,需要:取餐盘、排队、打不同的菜、送餐盘。类图如下:

代码如下:
public abstract class MakeDinnerAbstract {
//模板方法
public void TemplateMethod() {
getPlate();
quene();
getFood();
givePlate();
}
//具体方法1:取餐盘
public void getPlate() {
System.out.println("取餐盘");
}
//具体方法2:排队
public void quene() {
System.out.println("排队ing");
}
//抽象方法1:吃饭
public abstract void getFood();
//具体方法3:还餐盘
public void givePlate() {
System.out.println("还餐盘");
}
食堂吃饭的抽象类中,模板方法包含三个具体方法,一个抽象方法。
取米饭代码如下:
public class MakeRice extends MakeDinnerAbstract {
@Override
public void getFood() {
System.out.println("取米饭");
}
}
取面条代码如下:
public class MakeNoodle extends MakeDinnerAbstract {
@Override
public void getFood() {
System.out.println("取面条");
}
}
代码如下:
public static void main(String[] args) {
MakeRice mr = new MakeRice();
MakeNoodle mn = new MakeNoodle();
mr.TemplateMethod();
System.out.println("------------------");
mn.TemplateMethod();
}
执行结果如下:
取餐盘
排队ing
取米饭
还餐盘
------------------
取餐盘
排队ing
取面条
还餐盘
优点:
1、不变部分封装到父类中实现,而把可变部分由子类继承实现,便于子类继续扩展。不变部分便于代码复用。
2、子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
1、对每个不同的实现都需要定义一个子类,增加了系统实现的复杂度。
2、父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。