1. 模板方法模式:框架设计中的"骨架"艺术
第一次接触模板方法模式是在重构一个电商订单处理系统时。当时系统里有十几种订单类型,每种订单的处理流程大同小异,但代码却散落在各个角落。当我尝试用模板方法模式重构后,代码量减少了40%,维护成本直线下降——这就是设计模式的魅力所在。
模板方法模式属于行为型设计模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。就像建筑工地的施工蓝图,模板方法规定了主体结构(如地基、承重墙),同时允许装修风格根据需求变化。这种模式特别适合处理具有固定流程但存在局部差异的业务场景。
提示:当你在多个类中发现相似的算法结构,只是某些具体步骤实现不同时,就该考虑模板方法模式了。
2. 模式结构与核心组件
2.1 UML类图解析
典型的模板方法模式包含两个核心角色:
-
抽象类(AbstractClass):
- 定义算法骨架的模板方法(通常标记为final)
- 包含具体方法(已实现的通用步骤)
- 声明抽象方法(必须由子类实现的步骤)
- 可能包含钩子方法(可选步骤)
-
具体子类(ConcreteClass):
- 实现父类定义的抽象方法
- 可覆盖钩子方法改变默认行为
java复制// 抽象类示例
public abstract class OrderProcessor {
// 模板方法(final防止子类修改算法结构)
public final void processOrder() {
validateOrder();
calculateTotal();
applyDiscount();
processPayment();
notifyCustomer();
}
// 具体方法(通用实现)
private void validateOrder() {
System.out.println("基础订单验证逻辑");
}
// 抽象方法(子类必须实现)
protected abstract void applyDiscount();
// 钩子方法(可选步骤)
protected void notifyCustomer() {
// 默认实现
}
}
2.2 方法类型详解
-
模板方法:
- 使用final修饰确保算法结构不被破坏
- 调用顺序定义了业务流程图
- 好莱坞原则:"别找我们,我们找你"
-
具体方法:
- 包含所有子类共用的实现
- 通常声明为private防止子类修改
-
抽象方法:
- 强制子类提供特定实现
- 代表流程中的可变部分
-
钩子方法:
- 提供默认实现的可选步骤
- 子类可通过覆盖改变行为
- 命名通常以"do"或"on"开头(如doBeforeProcess)
3. 实战应用:跨平台文件导出系统
3.1 场景分析
假设我们需要开发一个支持PDF/Excel/CSV导出的报表系统,导出流程固定为:
- 准备数据
- 验证数据
- 格式化数据
- 写入文件
- 后处理(如压缩、加密)
只有步骤3和4的实现因格式而异,这正是模板方法的用武之地。
3.2 完整实现代码
java复制public abstract class ReportExporter {
// 模板方法
public final File exportReport(String reportName) {
Data data = prepareData(reportName);
validate(data);
String formatted = format(data);
File file = writeToFile(formatted);
postProcess(file);
return file;
}
private Data prepareData(String name) {
// 从数据库获取数据
return DB.query(name);
}
private void validate(Data data) {
if (data == null) throw new IllegalArgumentException();
}
// 抽象方法
protected abstract String format(Data data);
protected abstract File writeToFile(String content);
// 钩子方法
protected void postProcess(File file) {
// 默认不进行后处理
}
}
// PDF导出实现
public class PdfExporter extends ReportExporter {
@Override
protected String format(Data data) {
return PDFRenderer.render(data);
}
@Override
protected File writeToFile(String content) {
return FileUtils.write("report.pdf", content);
}
@Override
protected void postProcess(File file) {
// PDF需要加密
Encryptor.encrypt(file);
}
}
3.3 性能优化技巧
-
模板方法缓存:
java复制public abstract class CachedExporter extends ReportExporter { private Map<String, File> cache = new HashMap<>(); public final File exportReport(String reportName) { return cache.computeIfAbsent(reportName, name -> super.exportReport(name)); } } -
并行化处理:
java复制public abstract class ParallelExporter extends ReportExporter { public final CompletableFuture<File> exportAsync(String name) { return CompletableFuture.supplyAsync( () -> exportReport(name)); } }
4. 模式对比与选型指南
4.1 相似模式辨析
| 模式 | 关注点 | 灵活性 | 适用场景 |
|---|---|---|---|
| 模板方法 | 算法步骤扩展 | 中等 | 有固定流程的算法 |
| 策略模式 | 完整算法替换 | 高 | 需要动态切换算法 |
| 工厂方法 | 对象创建 | 低 | 需要解耦对象创建 |
4.2 使用场景判断
适用情况:
- 多个类包含相同算法,只有部分步骤不同
- 需要控制子类扩展点,防止算法结构被破坏
- 存在可复用的公共代码需要提取
不适用情况:
- 算法步骤经常变化(考虑策略模式)
- 需要运行时动态切换算法(考虑策略模式)
- 子类需要大幅修改父类行为(考虑组合模式)
5. 高级应用与模式变体
5.1 带回调的模板方法
java复制public class PaymentProcessor {
public final void process(Payment payment,
Consumer<Payment> preProcess,
Consumer<Payment> postProcess) {
preProcess.accept(payment);
executePayment(payment);
postProcess.accept(payment);
}
private void executePayment(Payment p) {
// 核心支付逻辑
}
}
// 使用示例
processor.process(payment,
p -> log.info("开始处理支付"),
p -> sendReceipt(p.getEmail())
);
5.2 模板方法链
java复制public abstract class Middleware {
private Middleware next;
public Middleware linkWith(Middleware next) {
this.next = next;
return next;
}
public abstract boolean check(String email, String password);
protected boolean checkNext(String email, String password) {
return next == null ? true : next.check(email, password);
}
}
// 使用示例
Middleware chain = new ThrottlingMiddleware()
.linkWith(new AuthMiddleware())
.linkWith(new RoleCheckMiddleware());
6. 常见陷阱与最佳实践
6.1 典型错误案例
-
过度使用抽象方法:
java复制// 反模式:每个步骤都是抽象的,失去模板意义 public abstract class BadTemplate { public final void process() { step1(); step2(); step3(); } public abstract void step1(); public abstract void step2(); public abstract void step3(); } -
忽视final关键字:
java复制// 危险:子类可能重写并破坏算法结构 public void processOrder() { // 非final方法 }
6.2 最佳实践清单
-
模板方法设计原则:
- 保持模板方法简短(不超过10个步骤)
- 抽象方法控制在3-5个以内
- 使用钩子方法提供可控扩展点
-
命名规范:
- 模板方法使用动词短语(processOrder)
- 钩子方法以"do"/"on"前缀(doBeforeSave)
- 抽象方法描述"做什么"而非"怎么做"
-
文档要求:
java复制/** * 订单处理模板 * 算法流程: * 1. 验证订单(具体方法) * 2. 计算总额(具体方法) * 3. 应用折扣(抽象方法) * 4. 处理支付(具体方法) * 5. 通知客户(钩子方法) */ public abstract class OrderProcessor { // ... }
7. 行业应用案例深度解析
7.1 Spring框架中的模板方法
-
JdbcTemplate:
java复制public <T> T execute(ConnectionCallback<T> action) { Connection con = getConnection(); try { return action.doInConnection(con); } finally { releaseConnection(con); } } -
RestTemplate:
java复制protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) { // 处理HTTP连接 // 调用回调函数 // 处理响应 }
7.2 Java集合框架示例
java复制// AbstractList中的模板方法
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheck(index);
boolean modified = false;
for (E e : c) {
add(index++, e); // 抽象方法
modified = true;
}
return modified;
}
8. 测试策略与Mock技巧
8.1 单元测试模板方法
java复制public class OrderProcessorTest {
@Test
public void testProcessFlow() {
OrderProcessor processor = new TestProcessor();
processor.processOrder();
// 验证流程顺序
verify(processor).validateOrder();
verify(processor).calculateTotal();
// ...
}
class TestProcessor extends OrderProcessor {
// 实现抽象方法...
}
}
8.2 使用Mock对象测试钩子
java复制@Test
public void testHookOverride() {
OrderProcessor processor = spy(new ConcreteProcessor());
// 覆盖钩子方法
doNothing().when(processor).notifyCustomer();
processor.processOrder();
verify(processor, never()).notifyCustomer();
}
9. 模式演进与现代化改造
9.1 函数式编程实现
java复制public class FunctionalTemplate {
private final Runnable preProcess;
private final Runnable postProcess;
public void process() {
preProcess.run();
coreProcess();
postProcess.run();
}
private void coreProcess() {
// 固定逻辑
}
}
// 使用示例
new FunctionalTemplate(
() -> log.info("开始"),
() -> log.info("结束")
).process();
9.2 响应式模板方法
java复制public abstract class ReactiveTemplate {
public Mono<Void> execute() {
return prepare()
.then(validate())
.then(process())
.doOnSuccess(this::notify);
}
protected abstract Mono<Data> process();
// 其他方法...
}
10. 设计模式组合应用
10.1 模板方法+工厂方法
java复制public abstract class DocumentGenerator {
public final Document generate() {
Header header = createHeader();
Content content = createContent();
Footer footer = createFooter();
return assemble(header, content, footer);
}
protected abstract Header createHeader();
protected abstract Content createContent();
protected abstract Footer createFooter();
private Document assemble(Header h, Content c, Footer f) {
// 组装逻辑
}
}
10.2 模板方法+策略模式
java复制public class ReportGenerator {
private ExportStrategy strategy;
public final void generateReport() {
prepareData();
strategy.export(data); // 策略模式
cleanup();
}
// 模板方法部分...
}
在多年实践中,我发现模板方法模式最容易被误用的地方是过度设计——不是所有需要复用的代码都适合用模板方法。一个简单的判断标准是:如果业务流程图可以用不超过5个主要步骤描述,且其中至少有2个步骤需要变化,这时候使用模板方法模式才能发挥最大价值。
