问题
什么是模板方法模式?
答案
1. 核心概念
模板方法模式(Template Method Pattern) 是一种行为型设计模式,在父类中定义算法的骨架(模板),将某些步骤延迟到子类实现。模板方法使得子类可以在不改变算法整体结构的情况下,重新定义算法的某些特定步骤。
核心思想:
- 父类:定义算法流程框架(模板方法),控制执行顺序
- 子类:实现具体步骤(抽象方法),不改变流程结构
典型应用场景:
- 数据访问层的通用操作流程(打开连接 → 执行 SQL → 关闭连接)
- HTTP 请求处理流程(解析请求 → 业务处理 → 返回响应)
- Spring 的 JdbcTemplate、RestTemplate
- Servlet 的 service() 方法
2. 实现原理与代码示例
基础实现
// 抽象类:定义模板方法
abstract class DataProcessor {
// 模板方法:定义算法骨架(final 防止子类修改流程)
public final void process() {
openConnection(); // 步骤1:打开连接
extractData(); // 步骤2:提取数据(子类实现)
processData(); // 步骤3:处理数据(子类实现)
closeConnection(); // 步骤4:关闭连接
}
// 通用步骤:父类实现
private void openConnection() {
System.out.println("打开数据连接");
}
// 抽象步骤:子类必须实现
protected abstract void extractData();
protected abstract void processData();
// 通用步骤:父类实现
private void closeConnection() {
System.out.println("关闭数据连接");
}
}
// 具体子类1:CSV 数据处理
class CsvDataProcessor extends DataProcessor {
@Override
protected void extractData() {
System.out.println("从 CSV 文件提取数据");
}
@Override
protected void processData() {
System.out.println("处理 CSV 数据格式");
}
}
// 具体子类2:JSON 数据处理
class JsonDataProcessor extends DataProcessor {
@Override
protected void extractData() {
System.out.println("从 JSON 文件提取数据");
}
@Override
protected void processData() {
System.out.println("处理 JSON 数据格式");
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
DataProcessor csvProcessor = new CsvDataProcessor();
csvProcessor.process();
System.out.println("---");
DataProcessor jsonProcessor = new JsonDataProcessor();
jsonProcessor.process();
}
}
输出结果:
打开数据连接
从 CSV 文件提取数据
处理 CSV 数据格式
关闭数据连接
---
打开数据连接
从 JSON 文件提取数据
处理 JSON 数据格式
关闭数据连接
3. 钩子方法(Hook Method)
钩子方法是一种特殊的模板方法技巧,允许子类通过覆盖钩子方法来影响模板流程,但不强制子类实现。
abstract class DataProcessor {
// 模板方法
public final void process() {
openConnection();
extractData();
processData();
// 钩子方法:可选的数据验证步骤
if (needValidation()) {
validateData();
}
closeConnection();
}
// 通用步骤
private void openConnection() {
System.out.println("打开数据连接");
}
// 抽象步骤
protected abstract void extractData();
protected abstract void processData();
// 钩子方法:默认实现,子类可选择覆盖
protected boolean needValidation() {
return false; // 默认不需要验证
}
protected void validateData() {
System.out.println("验证数据完整性");
}
private void closeConnection() {
System.out.println("关闭数据连接");
}
}
// 子类可以通过钩子方法控制流程
class SecureCsvProcessor extends DataProcessor {
@Override
protected void extractData() {
System.out.println("从 CSV 文件提取数据");
}
@Override
protected void processData() {
System.out.println("处理 CSV 数据格式");
}
// 覆盖钩子方法,启用验证
@Override
protected boolean needValidation() {
return true;
}
}
钩子方法特点:
- 提供默认实现(可以是空方法或返回默认值)
- 子类可选择性覆盖
- 用于控制模板流程的可选步骤
4. Spring 中的模板方法模式
JdbcTemplate
public class JdbcTemplate {
// 模板方法:定义 JDBC 操作流程
public <T> T execute(ConnectionCallback<T> action) {
Connection con = null;
try {
// 步骤1:获取连接
con = getConnection();
// 步骤2:执行具体操作(由回调对象实现)
return action.doInConnection(con);
} catch (SQLException ex) {
// 步骤3:异常处理
throw new DataAccessException(ex);
} finally {
// 步骤4:释放连接
releaseConnection(con);
}
}
private Connection getConnection() { ... }
private void releaseConnection(Connection con) { ... }
}
// 使用示例
jdbcTemplate.execute(new ConnectionCallback<Integer>() {
public Integer doInConnection(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM users");
ResultSet rs = ps.executeQuery();
rs.next();
return rs.getInt(1);
}
});
优势:
- 自动管理连接和资源释放
- 统一异常处理
- 简化 JDBC 样板代码
HttpServlet
public abstract class HttpServlet {
// 模板方法
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if (method.equals("GET")) {
doGet(req, resp);
} else if (method.equals("POST")) {
doPost(req, resp);
} else if (method.equals("PUT")) {
doPut(req, resp);
}
// ... 其他 HTTP 方法
}
// 抽象方法:子类实现具体的业务逻辑
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 默认实现:返回 405 Method Not Allowed
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// 默认实现
}
}
// 子类只需实现需要的方法
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 处理 GET 请求
}
}
AbstractApplicationContext
public abstract class AbstractApplicationContext {
// 模板方法:定义容器刷新流程
public void refresh() {
prepareRefresh(); // 刷新前准备
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory); // 钩子方法
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh(); // 钩子方法(子类扩展点)
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
} catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
}
}
// 钩子方法:子类可覆盖
protected void onRefresh() throws BeansException {
// 默认为空,子类可扩展
}
}
5. 模板方法 vs 策略模式
| 对比维度 | 模板方法模式 | 策略模式 |
|---|---|---|
| 实现方式 | 继承(父类定义流程) | 组合(持有策略对象) |
| 流程控制 | 父类控制整体流程 | 客户端控制策略选择 |
| 扩展方式 | 子类覆盖方法 | 新增策略类 |
| 耦合度 | 子类依赖父类(高) | 策略间独立(低) |
| 适用场景 | 流程固定,步骤可变 | 算法可互换 |
6. 优缺点分析
优点:
- ✅ 复用通用代码,减少重复
- ✅ 控制流程顺序,确保执行规范
- ✅ 符合开闭原则(新增子类扩展功能)
- ✅ 符合里氏替换原则(子类可替换父类)
缺点:
- ❌ 增加类的数量(每个变体需要一个子类)
- ❌ 继承的强耦合(子类依赖父类实现)
- ❌ 违反依赖倒置原则(依赖具体的父类)
7. 答题总结
简洁回答模板:
“模板方法模式在父类中定义算法骨架,将某些步骤延迟到子类实现,保证流程一致性。
核心结构:
- 父类:定义模板方法(final)和算法流程
- 抽象方法:子类必须实现的步骤
- 钩子方法:子类可选择性覆盖的扩展点
典型应用:
- Spring 的 JdbcTemplate、RestTemplate(封装资源管理)
- HttpServlet 的 service() 方法(HTTP 请求分发)
- AbstractApplicationContext 的 refresh() 方法(容器初始化流程)
与策略模式的区别:
- 模板方法用继承,父类控制流程
- 策略模式用组合,客户端选择算法
适用于流程固定但步骤实现多样的场景,能有效复用代码并保证执行规范。”