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