模板方法模式详解
一、模式定义
模板方法模式(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