TemplateMethod パターン — Spring JdbcTemplate に見る典型実装
処理の骨格は親、差分は子に書き分ける Template Method。Spring の `*Template` 系の設計思想と、Strategy / Decorator との使い分け。

どういう問題を解くパターンか
処理の 「骨格」 (呼び出し順序や共通処理) は親クラスに固定し、 「差分」 (個別のビジネスロジック) だけサブクラスに書かせる パターン。
典型例:
- ファイル読み込み: open → read → close の順序は固定、
read()の中身だけが型ごとに違う - トランザクション: 開始 → 業務処理 → コミット/ロールバックの枠は固定、業務処理だけ差分
- バッチジョブ: 事前チェック → 本処理 → 事後処理の順序は固定、本処理だけ差分
骨格を親クラスに final で書く のが Template Method の肝。
子クラスから順序を壊させません。
いつ使う / 使わない
| 使うべき | 使わないほうがよい |
|---|---|
| 処理順序が 絶対に固定 | 順序も含めて差し替えたい |
| 差分が常に 1 ヶ所に閉じている | 差分が多方向に広がる |
| ユースケースが継承で素直に表現できる | コンポジションの方が自然 |
Strategy との違い: Strategy は 「アルゴリズム全体を差し替える」、Template Method は 「骨格の一部だけを差し替える」。 たとえば データ出力の順序 (ヘッダ→ボディ→フッタ) は固定で、ボディの作り方だけ差し替えたい なら Template Method。 出力形式ごと丸ごと切り替えたい (CSV と JSON を切り替え) なら Strategy。
実装
抽象クラス
public abstract class ReportExporter {
/** 骨格メソッド (final で順序を保護) */
public final void export(OutputStream out) throws IOException {
openHeader(out);
writeBody(out);
closeFooter(out);
}
protected abstract void openHeader(OutputStream out) throws IOException;
protected abstract void writeBody(OutputStream out) throws IOException;
protected abstract void closeFooter(OutputStream out) throws IOException;
}
具象クラス
public class CsvReportExporter extends ReportExporter {
private final List<Row> rows;
public CsvReportExporter(List<Row> rows) {
this.rows = rows;
}
@Override
protected void openHeader(OutputStream out) throws IOException {
out.write("id,name,amount\n".getBytes(StandardCharsets.UTF_8));
}
@Override
protected void writeBody(OutputStream out) throws IOException {
for (Row r : rows) {
String line = r.id() + "," + r.name() + "," + r.amount() + "\n";
out.write(line.getBytes(StandardCharsets.UTF_8));
}
}
@Override
protected void closeFooter(OutputStream out) throws IOException {
// CSV は特に何もしない
}
}
public class JsonReportExporter extends ReportExporter {
private final List<Row> rows;
public JsonReportExporter(List<Row> rows) {
this.rows = rows;
}
@Override protected void openHeader(OutputStream out) throws IOException {
out.write("{\"items\":[".getBytes(StandardCharsets.UTF_8));
}
@Override protected void writeBody(OutputStream out) throws IOException {
// ... items を JSON で書き出す
}
@Override protected void closeFooter(OutputStream out) throws IOException {
out.write("]}".getBytes(StandardCharsets.UTF_8));
}
}
呼び出し側
ReportExporter exporter = new CsvReportExporter(rows);
exporter.export(outputStream);
骨格 (openHeader → writeBody → closeFooter の順序) はクライアント側では意識不要です。
Spring JdbcTemplate が完璧な実例
Spring の JdbcTemplate は Template Method の教科書的な実装です。
// Spring 側 (擬似的な骨格)
public <T> T execute(ConnectionCallback<T> callback) {
Connection con = dataSource.getConnection(); // 骨格: 接続取得
try {
return callback.doInConnection(con); // 差分: ユーザーロジック
} catch (SQLException e) {
throw translateException(e); // 骨格: 例外変換
} finally {
releaseConnection(con); // 骨格: クローズ
}
}
ユーザーは ConnectionCallback に業務処理だけ書けばよく、
接続取得 → 業務 → 例外変換 → 解放 という骨格は Spring 側に閉じ込められています。
同様に RestTemplate, TransactionTemplate, KafkaTemplate などの *Template クラスは
すべて Template Method パターンです。
「〜Template」 という命名はこのパターン由来です。
コールバック (Lambda) で近代化する
伝統的な Template Method は 「継承ありき」 ですが、 現代の Java では コールバックに差分を渡す ことで、継承せずに同じ構造を作れます。
public final class ReportFlow {
public static void run(
OutputStream out,
IoAction openHeader,
IoAction writeBody,
IoAction closeFooter
) throws IOException {
openHeader.accept(out);
writeBody.accept(out);
closeFooter.accept(out);
}
@FunctionalInterface
interface IoAction {
void accept(OutputStream out) throws IOException;
}
}
// 呼び出し側
ReportFlow.run(out,
o -> o.write("id,name\n".getBytes()),
o -> rows.forEach(r -> write(o, r)),
o -> {}
);
判断軸:
- 骨格が複雑 (フック多数、親に状態持たせたい) → 伝統的な継承
- 骨格が単純 (3 ステップ程度) → コールバックで十分
落とし穴
1. 骨格を final にせずオーバーライドされる
子クラスで骨格メソッドまでオーバーライドされると、順序が崩れ、
「継承したつもりで同じフローのはずなのにバグる」 事態が起きます。
骨格は必ず final にしてください。
2. フックメソッドを多用して継承地獄
「こういう場合もあるかも」 で beforeXxx(), afterXxx(), onError() を乱立させると、
子クラスの実装者が「どこで何が呼ばれるのか」を追えなくなります。
フックは最小限 に。本当に必要になってから追加するのが安全。
3. 継承で捨てられない共通処理が重い
継承にすると、「骨格を使いたいだけなのに親の全処理を背負わされる」 ことがあります。 テストで親の初期化がネックになったら、Strategy や Decorator への移行 を検討。
まとめ: Template Method は 「処理の順序を強制する」 ための強力なパターン。
Spring の *Template 系の設計思想がまさにこれで、
「ユーザーは業務ロジックだけ書けばいい」API を作るときの基本テクニックです。
継承の重さが気になるなら、同じ構造をコールバックで組むのも手。
関連記事
- アルゴリズム·
AbstractFactory パターン — 「関連するオブジェクト群」を一式で切り替える
Factory Method との違いを 1 分で整理し、OS 別 UI / DB 別 Repository / テナント別実装など、実務での使いどころを示す。
- アルゴリズム·
Builder パターン — 引数 4 つを超えたら迷わず導入する
コンストラクタ引数が多いクラスの可読性を救う Builder。手書き・Lombok・record の併用パターンと、Fluent Builder でよくあるハマり所。
- アルゴリズム·
Prototype パターン — `clone()` と `copy constructor`、どっちを選ぶべきか
既存インスタンスを複製して「型 + 設定済み」を使い回す Prototype。Java の Cloneable の闇と、現代的な代替 (copy constructor / toBuilder / record withers)。