别再复制粘贴了:用「模板方法模式」封装你的算法骨架

别再复制粘贴了:用「模板方法模式」封装你的算法骨架


做开发最忌讳的就是「Ctrl+C, Ctrl+V」。当你发现两个业务类的代码有 80% 都是重合的时候,别再犹豫了,这绝对不是什么业务复杂,而是你的代码架构出了问题。

今天我们要聊的,就是这个能让你从重复劳动中解脱出来的神器:「模板方法模式(Template Method Pattern)」。

什么是「模板方法模式」?

如果用一句话来解释,它就是一份 「填空题试卷」。

在试卷(父类)中,题目的顺序和结构是固定的,这部分由出卷人决定。而具体的答案(子类实现),则留给学生去做。

核心逻辑:父类定义一个算法的「骨架」,允许子类在不改变算法结构的情况下,重新定义算法中的某些特定步骤。

真实场景案例:电商系统的「支付回执」处理

想象一下,你在做一个支付系统。不管是走支付宝支付、微信支付还是银行卡,整个支付成功的「回调流程」其实长得非常像:

  1. 记录原始报文(每个支付方式都得存日志,这是规矩)。
  2. 验证签名(支付宝和微信的验签算法不同,这是细节)。
  3. 解析业务数据(报文格式不同,解析逻辑也不同)。
  4. 更新订单状态(统统要把订单改为「已支付」)。
  5. 推送到 App(通用逻辑:告诉用户付钱成功了)。

传统写法:代码堆砌

如果你给支付宝写一个类,给微信写一个类,你会发现除了第 2 步和第 3 步,其他的代码你全在复制粘贴。这不仅写起来累,以后万一要加个逻辑(比如:除了推送 App 还要加个短信通知),你得翻箱倒柜改遍所有的类。

优雅写法:模板方法模式

我们先把这个「固定流程」在父类里定死。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 抽象父类:定义算法骨架
public abstract class PaymentHandler {

// 核心模板方法!标记为 final,防止子类乱改逻辑顺序
public final void handleCallback() {
logRequest(); // 1. 记录日志
if (verifySignature()) { // 2. 验签(子类填空)
parseData(); // 3. 解析数据(子类填空)
updateOrder(); // 4. 更新订单
sendNotification(); // 5. 发送通知
}
}

private void logRequest() {
System.out.println("Step 1: 记录原始支付回调报文...");
}

private void updateOrder() {
System.out.println("Step 4: 将订单状态设置为【已支付】...");
}

private void sendNotification() {
System.out.println("Step 5: 给用户发送 App 推送提醒...");
}

// 这些是子类必须自己去操心的「差异化」逻辑
protected abstract boolean verifySignature();
protected abstract void parseData();
}

现在,我们要写具体的支付工具时,只需要关心那两道「差异化」的填空题:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 支付宝处理
public class AliPayHandler extends PaymentHandler {
@Override
protected boolean verifySignature() {
System.out.println("支付宝:使用 RSA2 算法进行验签...");
return true;
}

@Override
protected void parseData() {
System.out.println("支付宝:从 JSON 报文中提取 trade_no...");
}
}

进阶玩法:灵魂「钩子(Hook)」

有时候,子类不仅想填空,还想「反客为主」。比如,有些支付是不需要发通知的。这时候我们引入一个「钩子函数」:

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class PaymentHandler {
public final void handleCallback() {
// ...前面的步骤
if (shouldSend()) { // 引用钩子
sendNotification();
}
}

// 默认发通知,子类如果不爽可以重写它
protected boolean shouldSend() {
return true;
}
}

如果某个子类想跳过通知,重写这个方法返回 false 即可。这种「反向控制」的设计理念,就是著名的 「好莱坞原则」:不要给我们打电话,我们会给你打电话。

总结:什么时候该用它?

  1. 流程固定,但细节多变:当你发现几个类的处理流程高度一致,但每个步骤的具体实现有差异。
  2. 代码去重:当你看到几个类里有一模一样的方法,只有中间一两个方法不一样,这就该「合体」重构了。
  3. 控制算法顺序:如果你希望不管谁来继承,都必须先执行 A 再执行 B,最后执行 C,那就用 final 模板方法把顺序锁死。

模板方法模式其实就是一种「对高度统一事务的顶层设计」。它让子类专注于差异化的实现,而不用去操心那些琐碎、重复的固定流程。正如人生一样,规律(父类)是固定的,但精彩(子类)则由你来填补。