模版方法
提供一个方法作为完成某类功能的“固定模板”,模板方法里把整体步骤按顺序封装好,但允许子类通过重写某些“钩子方法 / 抽象方法”来自定义部分细节。
解决什么问题:
- 提高代码复用性:把一类算法/业务流程中不变的部分固定在父类模板方法里,避免在每个子类中重复编写流程代码。
- 保留扩展点:把变化的步骤设计成抽象方法或可重写的方法,交给子类去实现,从而在不改变整体流程的前提下扩展行为。
- 让调用者面向抽象编程:上层代码只调用模板方法,不关心具体是哪一个子类,实现“对扩展开放,对修改关闭”。
怎么写:
实现步骤可以记成三句话:
- 在抽象父类中定义一个模板方法:模板方法中按固定顺序调用若干步骤方法(有些是具体的,有些是抽象的),通常把模板方法声明为
final,禁止子类改动整体流程。 - 在抽象父类中定义抽象方法 / 钩子方法:这些方法只给出“要做什么”的规范,不写具体实现,相当于留给子类的插槽。
- 在子类中重写抽象方法 / 钩子方法:根据自己的需求实现差异化逻辑,但整体调用流程仍由父类模板方法控制。
使用场景:
- 需要在多个子类之间共享相同的处理流程,但某些步骤的实现细节不同;
- 希望把“算法框架”固化在父类中,只允许子类修改其中的某些步骤;
- 典型例子:文档生成流程、报表导出流程、钩子型回调流程、Spring 中的各种
XXXTemplate。
示例:
// 抽象父类:定义写作文的模板
public abstract class People {
// 模板方法:固定作文的大致结构
public final void write() { // 通常加 final,防止子类修改整体流程
System.out.println("《我的爸爸》");
System.out.println("第一段:我爸爸是一个好人,我特别喜欢他,他对我很好...");
// 变化的部分:每个子类写自己眼中的爸爸
writeMain();
System.out.println("最后一段:我爸爸真好,你有这样的爸爸吗?");
}
// 抽象方法:留给子类实现具体内容
public abstract void writeMain();
}
// 子类 1:学生视角
public class Student extends People {
@Override
public void writeMain() {
System.out.println("中间段:我爸爸很牛逼,是个管理者,我开车不用看红绿灯。");
}
}
// 子类 2:老师视角
public class Teacher extends People {
@Override
public void writeMain() {
System.out.println("中间段:我爸爸经常让我站在这里别动,他要去买几斤桔子~~");
}
}
// 测试类:调用模板方法
public class Test {
public static void main(String[] args) {
People s = new Student();
s.write(); // 调用同一个模板方法,输出学生版作文
People t = new Teacher();
t.write(); // 调用同一个模板方法,输出老师版作文
}
}小结:模板方法模式 = 父类固定流程 + 子类重写步骤。
不变的流程写在父类,变化的细节交给子类,从而在保证结构统一的前提下,实现灵活多样的行为。