模板方法模式
在很多业务代码中,我们都会遇到这种场景:
- 整体流程是固定的
- 但流程中的某些步骤因业务类型不同而不同
- 又不希望子类随意修改流程顺序
如果你把这些逻辑全部写进一个类里,往往会出现:
- 类越来越大
- if / switch 越来越多
- 改一处逻辑容易影响其他场景
这正是 模板方法模式 要解决的问题。
一、问题从哪里来?
一开始,我有一个用于“保存代码文件”的工具类,大概长这样:
- 校验参数
- 创建唯一目录
- 把代码写成文件
- 返回目录路径
当前代码存在不同的报错逻辑:
- HTML 单文件是一套保存逻辑
- HTML + CSS + JS 又是一套保存逻辑
- 所有逻辑都堆在一个类里,越来越臃肿
导致:
这个类在不断变大,而且每加一种类型就要改原有代码。
二、什么是不变的?
整理之后会发现:
不变的部分:
- 保存流程是固定的
- 都要校验参数
- 都要创建目录
- 最后都返回一个目录
变化的部分:
- 写哪些文件
- 每个文件的内容来自哪里
这正好符合一句模版方法的逻辑:
流程固定,具体实现内容待定。
三、模板方法模式的核心想法
模板方法模式其实很简单:
把“流程”放在父类,把“变化”交给子类。
父类只做一件事:
规定顺序,不让子类乱来。
四、一个简化后的模板类
public abstract class CodeFileSaverTemplate<T> {
public final File saveCode(T result) {
validate(result);
String dir = createDir();
saveFiles(result, dir);
return new File(dir);
}
protected void validate(T result) {}
protected abstract void saveFiles(T result, String dir);
protected String createDir() {
return dir;
}
}
这里有三个关键信号:
saveCode()是final:流程不能被改【固定的模版流程】protected:只给子类用abstract:子类必须实现
五、子类只关心自己具体实现
HTML 保存
public class HtmlSaver extends CodeFileSaverTemplate<HtmlCodeResult> {
@Override
protected void saveFiles(HtmlCodeResult result, String dir) {
write(dir, "index.html", result.getHtmlCode());
}
}
多文件保存
public class MultiFileSaver extends CodeFileSaverTemplate<MultiFileCodeResult> {
@Override
protected void saveFiles(MultiFileCodeResult result, String dir) {
write(dir, "index.html", result.getHtmlCode());
write(dir, "style.css", result.getCssCode());
write(dir, "script.js", result.getJsCode());
}
}
子类不需要关心:
- 目录怎么建
- 校验顺序
- 返回值
只管一件事:
我要写哪些文件。
六、为什么不用 if / switch?
当然可以写成这样:
if (type == HTML) { ... }
else if (type == MULTI) { ... }
但问题是:
- 每加一种类型就要改这个类
- 老逻辑和新逻辑混在一起
- 长期一定失控
模板方法的好处是:
新增一种类型 = 新增一个类,不动旧代码。
七、什么时候该用模板方法?
适合用在:
- 流程天然有顺序
- 顺序不允许被破坏
- 变化点明确、可控
不适合用在:
- 流程差异非常大
- 需要频繁运行时切换逻辑
- 不想使用继承的场景