| 角色 | 說明 | 典型實作 |
| AbstractClass | 定義範本方法 (final)、抽象基本方法、具體基本方法 | 抽象類 |
| ConcreteClass | 實作抽象基本方法,完成特定步驟 | 具體子類 |
核心程式碼骨架
public abstract class DataProcessor {
// 範本方法:final 防止子類覆寫改變流程
public final void process() {
readData(); // 步驟 1: 讀取 (可變)
validateData(); // 步驟 2: 驗證 (可變)
transformData(); // 步驟 3: 轉換 (可變)
writeData(); // 步驟 4: 寫入 (可變)
cleanup(); // 步驟 5: 清理 (固定,可提供預設實作)
}
// 基本方法:抽象/具體/鉤子
protected abstract void readData();
protected abstract void validateData();
protected abstract void transformData();
protected abstract void writeData();
// 鉤子方法:預設空實作,子類可選擇性覆寫
protected void cleanup() {}
}
具體子類實作
public class CsvProcessor extends DataProcessor {
@Override protected void readData() {
System.out.println("讀取 CSV 檔案...");
}
@Override protected void validateData() {
System.out.println("驗證 CSV 格式、必填欄位...");
}
@Override protected void transformData() {
System.out.println("轉換為領域物件...");
}
@Override protected void writeData() {
System.out.println("寫入資料庫...");
}
@Override protected void cleanup() {
System.out.println("關閉 CSV 讀取器...");
}
}
public class JsonProcessor extends DataProcessor {
@Override protected void readData() { System.out.println("讀取 JSON..."); }
@Override protected void validateData() { System.out.println("驗證 JSON Schema..."); }
@Override protected void transformData() { System.out.println("反序列化..."); }
@Override protected void writeData() { System.out.println("發送到消息佇列..."); }
}
客戶端使用
public class Client {
public static void main(String[] args) {
DataProcessor p1 = new CsvProcessor();
p1.process(); // 執行完整流程
DataProcessor p2 = new JsonProcessor();
p2.process();
}
}
關鍵設計要點
- 範本方法加 final:鎖定演算法骨架,防止子類破壞流程順序。
- 基本方法分類:
抽象方法:子類必須實作。
具體方法:父類提供預設,子類可覆寫。
鉤子方法:預設空實作,子類決定是否介入 (如 cleanup)。
- 好萊塢原則:「Don't call us, we'll call you.」父類呼叫子類方法,控制權在父類。
- 存取修飾符:基本方法通常
protected,僅供子類/同包使用;範本方法 public 供客戶端呼叫。
實戰場景
1. Spring JdbcTemplate
// 範本方法: execute(query, resultSetExtractor)
// 基本方法: doInPreparedStatement(ps), extractData(rs)
jdbcTemplate.query("SELECT * FROM user", new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) { ... } // 子類實作
});
2. HttpServlet
// 範本方法: service(req, resp) (final)
// 基本方法: doGet, doPost, doPut, doDelete...
// 子類覆寫 doGet/doPost 實作具體邏輯
3. 輸入驗證流程
public abstract class Validator {
public final ValidationResult validate(Input input) {
if (!checkNull(input)) return invalid("非空");
if (!checkFormat(input)) return invalid("格式");
if (!checkBusiness(input)) return invalid("業務規則");
return valid();
}
protected boolean checkNull(Input i) { return i != null; }
protected abstract boolean checkFormat(Input i);
protected abstract boolean checkBusiness(Input i);
}
優缺點總結
| 優點 | 缺點 |
| ✅ 程式碼複用:共同流程集中在父類 | ❌ 類別爆炸:每個變化組合需一子類 |
| ✅ 流程控制:骨架固定,防止邏輯錯誤 | ❌ 靈活度受限:難以動態改變步驟順序 |
| ✅ 反向控制:父類呼叫子類,符合好萊塢原則 | ❌ 深層繼承:維護困難,建議組合替代 |
| ✅ 易於擴展:新增處理類型只需加子類 | |
現代替代方案
- 策略模式 + 組合:將每個步驟封裝為 Strategy 介面,流程類組合策略列表,支援執行時動態組裝。
- 責任鏈模式:每個步驟為一個 Handler,鏈式傳遞,可動態增減/重排步驟。
- Java 8+ 函數式介面:基本方法改為
Function/Function<T,R> 參數,避免建立子類。
總結
範本方法模式以「骨架固定、細節延遲」實現流程復用與擴展平衡。理解其結構、好萊塢原則、鉤子方法設計,能在框架開發、流程型業務 (ETL、訂單處理、驗證鏈) 中靈活應用。現代開發建議搭配組合/函數式手法,避免過深繼承體系。
- 上一篇文章: Java Web Servlet 開發總結 (二):ServletConfig、ServletContext 進階應用與資源存取
- 下一篇文章: Struts2 攔截 404 錯誤最佳實踐:web.xml + struts.xml 雙重防護
Copyright ©
程式碼之家 All Rights Reserved