模板方法模式详解
一、模式定义
模板方法模式(Template Method Pattern)定义一个操作中的算法骨架,将某些步骤延迟到子类实现。
二、核心结构
1. 抽象模板类
public abstract class AbstractTemplate {
// 模板方法(final防止子类覆盖)
public final void templateMethod() {
step1();
step2();
step3();
if(hook()) {
step4();
}
}
// 抽象方法(必须由子类实现)
protected abstract void step2();
// 具体方法(已有默认实现)
protected void step1() {
System.out.println("执行步骤1");
}
protected void step3() {
System.out.println("执行步骤3");
}
// 钩子方法(可选覆盖)
protected boolean hook() {
return true;
}
protected void step4() {
System.out.println("执行步骤4");
}
}
2. 具体实现类
public class ConcreteClassA extends AbstractTemplate {
protected void step2() {
System.out.println("A实现-步骤2");
}
protected boolean hook() {
return false; // 关闭步骤4
}
}
public class ConcreteClassB extends AbstractTemplate {
protected void step2() {
System.out.println("B实现-步骤2");
}
protected void step4() {
System.out.println("B定制-步骤4");
}
}
三、完整示例:饮料制作系统
1. 抽象饮料类
public abstract class BeverageTemplate {
// 模板方法(final)
public final void prepareBeverage() {
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()) {
addCondiments();
}
}
protected abstract void brew();
protected abstract void addCondiments();
protected void boilWater() {
System.out.println("煮沸水");
}
protected void pourInCup() {
System.out.println("倒入杯中");
}
// 钩子方法
protected boolean customerWantsCondiments() {
return true;
}
}
2. 具体饮料实现
// 咖啡
public class Coffee extends BeverageTemplate {
protected void brew() {
System.out.println("冲泡咖啡粉");
}
protected void addCondiments() {
System.out.println("加糖和牛奶");
}
protected boolean customerWantsCondiments() {
String answer = getUserInput();
return answer.toLowerCase().startsWith("y");
}
private String getUserInput() {
System.out.print("要加糖和牛奶吗(y/n)? ");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
return reader.readLine();
} catch (IOException e) {
return "no";
}
}
}
// 茶
public class Tea extends BeverageTemplate {
protected void brew() {
System.out.println("浸泡茶叶");
}
protected void addCondiments() {
System.out.println("加柠檬");
}
}
3. 客户端使用
public class BeverageTest {
public static void main(String[] args) {
System.out.println("制作咖啡...");
BeverageTemplate coffee = new Coffee();
coffee.prepareBeverage();
System.out.println("\n制作茶...");
BeverageTemplate tea = new Tea();
tea.prepareBeverage();
}
}
四、高级应用:数据库操作模板
1. 抽象DAO模板
public abstract class JdbcTemplate {
// 模板方法
public final <T> T execute(String sql, RowMapper<T> rowMapper) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(sql);
setParameters(stmt);
rs = stmt.executeQuery();
return rowMapper.mapRow(rs);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
closeResources(conn, stmt, rs);
}
}
protected abstract void setParameters(PreparedStatement stmt) throws SQLException;
protected Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test");
}
protected void closeResources(Connection conn, Statement stmt, ResultSet rs) {
try { if (rs != null) rs.close(); } catch (SQLException e) {}
try { if (stmt != null) stmt.close(); } catch (SQLException e) {}
try { if (conn != null) conn.close(); } catch (SQLException e) {}
}
}
2. 行映射接口
public interface RowMapper<T> {
T mapRow(ResultSet rs) throws SQLException;
}
3. 具体DAO实现
public class UserDao extends JdbcTemplate {
public User findById(long id) {
return execute("SELECT * FROM users WHERE id = ?", rs -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
return user;
});
}
protected void setParameters(PreparedStatement stmt) throws SQLException {
stmt.setLong(1, 1L); // 设置查询参数
}
}
五、模式优势
- 提高代码复用性
- 实现反向控制(好莱坞原则)
- 便于扩展和维护
- 符合
开闭原则
六、适用场景
- 多个类有相同算法结构
- 需要控制子类扩展点
- 存在公共行为需要抽取
- 框架设计中的流程控制
七、注意事项
- 模板方法应该声明为final
- 合理设计抽象方法和钩子方法
- 避免过度抽象导致复杂度增加
- 与策略模式区分使用场景
八、最佳实践
- 使用钩子方法提供灵活扩展点
- 保持模板方法简洁
- 合理命名抽象方法
- 考虑与工厂方法模式结合使用
- 为常用操作提供默认实现
九、完整示例代码结构
src/
├── main/
│ ├── java/
│ │ ├── template/
│ │ │ ├── AbstractTemplate.java
│ │ │ ├── ConcreteClassA.java
│ │ │ ├── ConcreteClassB.java
│ │ │ ├── BeverageTemplate.java
│ │ │ ├── Coffee.java
│ │ │ ├── Tea.java
│ │ │ ├── JdbcTemplate.java
│ │ │ ├── UserDao.java
│ │ │ └── BeverageTest.java