模板方法模式

模板方法模式(Template Method Pattern) 是一种行为型设计模式。

简单来说,它在一个方法中定义了一个算法的骨架(步骤),而将一些步骤的具体实现延迟到子类中。这样,子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。

核心思想:封装不变,扩展可变

想象一下“冲泡饮料”的过程:

  1. 烧开水(相同)
  2. 冲泡原料(不同:咖啡或茶叶)
  3. 倒入杯子(相同)
  4. 添加调料(不同:加糖或加柠檬)

在这个场景中,烧水和倒水是“不变”的结构,而冲泡什么、加什么调料是“可变”的细节。


模式结构

  1. 抽象类(Abstract Class):定义算法骨架。包含:

    • 模板方法:定义了算法的步骤(通常设为 final,防止子类修改骨架)。
    • 抽象方法:由子类实现的具体逻辑。
    • 钩子方法(Hook):默认不做事或提供默认实现,子类可以视情况重写,用来控制算法的流程。
  2. 具体实现类(Concrete Class):实现抽象类中的抽象方法,完成属于自己的特定逻辑。


代码示例:制作咖啡与茶

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 抽象类:饮料机
abstract class Beverage {
// 模板方法,决定了做饮料的固定步骤
public final void makeBeverage() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) { // 钩子方法控制
addCondiments();
}
}

// 固定步骤直接实现
private void boilWater() { System.out.println("烧开水"); }
private void pourInCup() { System.out.println("倒入杯中"); }

// 变化的步骤留给子类实现
abstract void brew();
abstract void addCondiments();

// 钩子方法,子类可以覆盖它
boolean customerWantsCondiments() { return true; }
}

// 具体实现:咖啡
class Coffee extends Beverage {
@Override
void brew() { System.out.println("用滤纸过滤咖啡末"); }
@Override
void addCondiments() { System.out.println("加糖和牛奶"); }
}

// 具体实现:茶
class Tea extends Beverage {
@Override
void brew() { System.out.println("浸泡茶叶"); }
@Override
void addCondiments() { System.out.println("加柠檬"); }

// 重写钩子,茶叶通常不加调料
@Override
boolean customerWantsCondiments() { return false; }
}

优缺点分析

优点 缺点
代码复用:将公共代码提取到父类。 类数量增加:每个不同的实现都需要一个子类。
扩展性好:增加新逻辑只需增加子类,符合开闭原则。 逻辑受限:子类必须遵守父类的骨架,灵活性受限。
控制权集中:算法结构在父类中统一管理。 理解成本:如果继承层次过深,代码阅读会变困难。

实际应用场景

  • Java ServletHttpServlet 中的 service() 方法定义了处理请求的流程,而 doGet()doPost() 交由开发者实现。
  • Spring 框架:各种 JdbcTemplateRestTemplate 也是该模式的变体应用。
  • JUnit:测试框架中定义的 setUp()tearDown() 钩子方法。

你想看一个关于如何在实际 Spring 项目中应用模板方法模式(例如处理不同类型的支付回调)的具体案例吗?

Spring Boot 开发中常用的设计模式

Spring Boot 本身不是“为了用设计模式而用设计模式”,但它的核心能力(IoC、AOP、自动配置、扩展点 SPI)天然就以一批经典模式为基础。理解这些模式能帮助你:

  • 更顺畅地读源码(知道扩展点在什么位置)
  • 写出更易扩展的业务代码(减少 if/else 和硬编码)
  • 正确使用框架能力(如事务、事件、过滤链、自动装配)

下面是 Spring Boot 开发中最常见、最实用的一组模式速览。


单例模式(Singleton)

核心:全局唯一实例,统一管理生命周期。

Spring 里的体现

  • 默认 Bean 作用域就是 singleton(一个 ApplicationContext 内单例)
  • 单例并不等于“线程安全”:单例 bean 经常是无状态的(stateless),以避免并发问题

常见实践:Service/Repository 作为单例 bean;把“请求相关状态”放到方法参数/局部变量,或使用 request 作用域。


工厂方法 / 抽象工厂(Factory Method / Abstract Factory)

核心:把“创建对象”这件事封装起来,调用方只关心“拿到能用的对象”。

Spring 里的体现

  • BeanFactory / ApplicationContext:从容器“获取对象”,你不直接 new
  • @Bean 方法本质上就是工厂方法:框架调用你的方法来创建 bean
  • FactoryBean<T>:把复杂对象创建逻辑封装到一个工厂 bean 中(比如代理对象、连接工厂等)

建造者模式(Builder)

核心:用链式调用逐步构建复杂对象,避免构造函数参数爆炸。

Spring/Boot 常见场景

  • SpringApplicationBuilder(启动参数/环境组合)
  • 各类 *Builder:如 UriComponentsBuilderWebClient.Builder(在 Spring WebFlux 中)

策略模式(Strategy)

核心:把“可替换的算法/规则”抽象成接口,运行时选择不同实现。

Spring 里的体现

  • 典型扩展点:ConverterHandlerMethodArgumentResolverHttpMessageConverterPasswordEncoderCacheManager
  • 业务里也非常常用:按支付渠道/活动规则/风控策略选择不同实现,替代大段 if-else

一个最小可用的业务例子(按渠道处理回调)

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
30
public interface PayCallbackHandler {
boolean supports(String channel);
void handle(String payload);
}

@Component
class WxPayCallbackHandler implements PayCallbackHandler {
public boolean supports(String channel) { return "wx".equals(channel); }
public void handle(String payload) { /* ... */ }
}

@Component
class AliPayCallbackHandler implements PayCallbackHandler {
public boolean supports(String channel) { return "ali".equals(channel); }
public void handle(String payload) { /* ... */ }
}

@Service
class PayCallbackDispatcher {
private final List<PayCallbackHandler> handlers;
PayCallbackDispatcher(List<PayCallbackHandler> handlers) { this.handlers = handlers; }

public void dispatch(String channel, String payload) {
handlers.stream()
.filter(h -> h.supports(channel))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unsupported channel: " + channel))
.handle(payload);
}
}

代理模式(Proxy)

核心:为目标对象提供一个“替身”,在不改目标代码的情况下增强能力(日志、事务、鉴权、缓存等)。

Spring 里最典型的体现

  • AOP:@Transactional@Async@Cacheable@Validated 等,大多数都是通过代理增强实现
  • 代理常见两类:JDK 动态代理(基于接口)和 CGLIB(基于子类)

实战注意:同类内部方法自调用(self-invocation)可能绕过代理,导致事务/缓存不生效。


观察者模式(Observer / 发布-订阅)

核心:事件发布者与订阅者解耦,发布者只“发事件”,订阅者各自处理。

Spring 里的体现

  • ApplicationEventPublisher + @EventListener
  • 适合做:异步通知、审计日志、站内消息、缓存刷新等(需要与主流程解耦的动作)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public record OrderPaidEvent(Long orderId) {}

@Service
class OrderService {
private final ApplicationEventPublisher publisher;
OrderService(ApplicationEventPublisher publisher) { this.publisher = publisher; }

public void pay(Long orderId) {
// 1) 更新订单为已支付 ...
publisher.publishEvent(new OrderPaidEvent(orderId));
}
}

@Component
class OrderPaidListener {
@EventListener
public void onPaid(OrderPaidEvent event) {
// 发券/发消息/记流水 ...
}
}

责任链模式(Chain of Responsibility)

核心:把一系列处理步骤串成链,每个节点只关注自己的一段逻辑,且可以选择“放行/拦截”。

Spring 里的体现

  • Servlet Filter 链(Filter
  • Spring MVC 拦截器链(HandlerInterceptor
  • Spring Security Filter Chain(非常典型的责任链)

业务里常见:参数校验 → 幂等校验 → 风控 → 业务处理 → 审计/通知。


适配器模式(Adapter)

核心:把一个接口“转换成”另一个接口,让不兼容的组件能协同工作。

Spring 里的体现

  • Spring MVC 的 HandlerAdapter:统一适配不同类型的 Controller(让 DispatcherServlet 只面向统一调用入口)
  • 各类 *Adapter/*Configurer:对外暴露更友好的扩展接口,同时内部保持稳定实现

装饰器模式(Decorator)

核心:在不改变原对象类型/调用方式的前提下,动态叠加增强能力。

Spring 里的常见体现

  • Servlet API 的 HttpServletRequestWrapper / HttpServletResponseWrapper(对请求/响应做包装增强)
  • 各类“包装型”实现:在保留原始对象能力的同时叠加监控、埋点、限流等

门面模式(Facade)

核心:为复杂子系统提供一个简单统一的入口。

Spring 里的体现

  • JdbcTemplate:对 JDBC 的繁琐流程做门面封装(连接、异常转换、资源释放等)
  • RestTemplate(或 WebClient):对 HTTP 调用提供更统一的高层 API

模板方法模式(Template Method)在 Spring 里的延伸

你上面已经详细介绍了模板方法模式;在 Spring 生态里常见“模板类”还有:

  • JdbcTemplate:固定流程 + 回调扩展(RowMapper/PreparedStatementSetter 等)
  • RedisTemplate:固定序列化/执行流程 + 提供扩展点
  • TransactionTemplate:固定事务边界流程 + 回调执行业务

这类模板类的价值是:把资源管理、异常转换、边界控制等“重复且容易错”的部分收敛到框架层。