---
title: "テンプレートメソッドパターンとは？Java・Python・Spring Bootの実装例とStrategyとの違い"
url: "https://www.issoh.co.jp/tech/details/3420/"
published: 2024-08-29
updated: 2026-11-01
categories: ["アーキテクチャ"]
publisher: "株式会社一創"
---

# テンプレートメソッドパターンとは？Java・Python・Spring Bootの実装例とStrategyとの違い

テンプレートメソッドパターンは、処理の手順を親クラスに固定し、変わる部分だけをサブクラスに書かせるGoFの振る舞いパターンです。「穴あきの処理フロー」と考えると早いでしょう。この記事では、JDK 25とPython 3.14.6で実行したコードで構造を示し、Spring Boot 4.1.2で組むときに手順メソッドを`final`にすると`@Transactional`が効かなくなる実測結果、Springの`OncePerRequestFilter`やJdbcTemplateがこのパターンに当たるのかまで、公式ドキュメントの記述をもとに判断します。

## まとめ：テンプレートメソッドパターンの要点

- 手順を固定する**テンプレートメソッド**、実装必須の**抽象メソッド**、既定実装を持つ**フックメソッド**の3つで組み立てる
- Javaは`final`と`abstract`で手順の上書きも実装漏れもコンパイル時に止められる。Pythonの`@final`は型チェッカー向けの表明で、実行時は素通りする
- Spring Bootでは、`@Transactional`を付けたテンプレートメソッドを`final`にしない。CGLIBではそのメソッドにアドバイスを適用できず、本記事の検証構成ではトランザクションが開始されず、プロキシ側のフィールドも`null`になった
- Springの`OncePerRequestFilter`は継承型のテンプレートメソッド、JdbcTemplateは可変部をコールバックで受け取る別の差し込み方
- 手順が本当に不変で、差分が1〜2箇所に収まるときだけ使う。可変部を実行時に切り替えたいならStrategyパターンを選ぶ

## テンプレートメソッドパターンの構造：手順・抽象メソッド・フックの3役

### 3つの役割と呼び出しの向き

親クラスに、処理の順番だけを書いたメソッドを1本用意します。これがテンプレートメソッドです。その中から呼ばれる個々のステップのうち、サブクラスごとに必ず変わるものを抽象メソッドとして宣言し、たいていは共通だが時々変えたいものをフックメソッドとして既定実装つきで置きます。

| 役割         | 親クラスでの書き方 | サブクラスでの扱い   |
| ---------- | --------- | ----------- |
| テンプレートメソッド | 共通の手順を実装  | 原則として上書きしない |
| 抽象メソッド     | 宣言のみ      | 実装が必須       |
| フックメソッド    | 既定の実装つき   | 必要なときだけ上書き  |

呼び出しの向きに注目してください。利用側が呼ぶのは親クラスのテンプレートメソッドだけで、サブクラスのメソッドを呼ぶのは親クラスのほうです。サブクラスは「呼ばれるのを待つ部品」に徹します。手順の主導権を親クラス（あるいはフレームワーク）が握るこの構図は、制御の反転（IoC）の一例として説明されます。Springがこの考え方をコンテナ全体に広げた仕組みは[IoCコンテナとは？制御の反転の仕組みとSpring IoCコンテナ・Beanのスコープとライフサイクルを動くコードで解説](/tech/details/3210/)で扱っています。

設計で迷うのは抽象メソッドとフックメソッドの線引きです。「サブクラスが必ず答えを持っている問い」だけを抽象メソッドにします。既定値が決められる時点で、それは抽象メソッドではなくフックメソッドです。

### GoF23パターンでの位置づけと「テンプレートパターン」という呼び名

出典はGamma・Helm・Johnson・Vlissidesによる書籍『Design Patterns: Elements of Reusable Object-Oriented Software』です。出版社Pearson（InformIT）の書誌ページによれば、同書がカタログ化したパターンは23種類で、生成・構造・振る舞いの3章に分かれています。テンプレートメソッドが属するのは振る舞いに関するパターンです。23種の全体像は[GoFデザインパターン23種類の一覧｜3分類と見分け方をJavaコードで解説](/tech/details/2615/)にまとめています。

検索では「テンプレートパターン」「テンプレートメソッド」「Template Methodパターン」と表記が割れますが、どれも同じパターンです。テンプレートは「ひな形」の意味で、ここでは処理手順のひな形を親クラスが持つことを指します。単に「テンプレート」と書くとテンプレートエンジンやHTMLテンプレートと混同されるので、設計ドキュメントでは Template Method か「テンプレートメソッドパターン」まで書き切ってください。

## Javaでの実装：finalによる手順の保護とabstractによる実装の強制

帳票の生成を例にします。「読み込む → 不正な行を除く → 出力形式に整える」という順番は変わらず、変わるのは読み込み方と出力形式です。順番をテンプレートメソッドに閉じ込め、読み込みと出力を抽象メソッドに、除外条件をフックメソッドにします。このJava例は単純なカンマ区切り入力を対象とし、引用符で囲まれたカンマやフィールド内改行を含むCSVの解析は扱いません。

```
import java.util.List;
import java.util.stream.Collectors;

abstract class ReportPipeline {
    public final String run(String csv) {                 // テンプレートメソッド：手順を固定
        List<String[]> rows = load(csv).stream().filter(this::isValid).toList();
        return render(rows);
    }
    protected abstract List<String[]> load(String csv);   // 抽象メソッド：実装必須
    protected abstract String render(List<String[]> rows);
    protected boolean isValid(String[] row) {             // フックメソッド：既定の実装を持つ
        return !row[0].isEmpty();
    }
}

class CsvReport extends ReportPipeline {
    @Override protected List<String[]> load(String csv) {
        return csv.lines().skip(1).map(l -> l.split(",", -1)).toList();
    }
    @Override protected String render(List<String[]> rows) {
        return rows.stream().map(r -> r[0]).collect(Collectors.joining(" / "));
    }
}

class DevOnlyReport extends CsvReport {
    @Override protected boolean isValid(String[] row) {   // フックだけ差し替える
        return row.length >= 2 && super.isValid(row) && row[1].equals("dev");
    }
}

public class Main {
    public static void main(String[] args) {
        String src = "name,dept\nsato,dev\n,sales\nkato,sales\n";
        System.out.println(new CsvReport().run(src));
        System.out.println(new DevOnlyReport().run(src));
    }
}
```

OpenJDK 25.0.4.1で`java Main.java`として実行した結果です。

```
sato / kato
sato
```

`DevOnlyReport`が書いたのは`isValid`の3行だけで、読み込みも出力も`CsvReport`のものがそのまま動いています。フックの差し替えだけで振る舞いが変わるのが、このパターンの実利です。抽象メソッドを`protected`にしているのは、`load`や`render`が部品であり外から単体で呼ばれるべきではないからです。公開するのは`run`だけにすると、呼び出し側との契約が1本になります。

Javaでは骨組みの保護をコンパイラに任せられます。`run`を上書きするサブクラスと、抽象メソッドを実装し忘れたサブクラスをそれぞれ`javac`に通すと、どちらもコンパイルエラーで止まりました。

```
Bad.java:6: error: run() in Broken cannot override run() in Base
    @Override public String run() { return "skip"; }
                            ^
  overridden method is final

Abs.java:2: error: Missing is not abstract and does not override abstract method step() in Base2
```

手順を守らせる強制力が言語機能で担保される。ここが、共通処理をコピーして揃える運用との決定的な差です。

## Pythonでの実装：abc.ABCによる抽象化と@finalの実行時制約

Pythonには抽象メソッドを表す構文キーワードが無いため、標準ライブラリの`abc`モジュールを使います。`ABC`を継承し、実装必須のメソッドに`@abstractmethod`を付けます。

```
import csv, io
from abc import ABC, abstractmethod
from typing import final

class ReportPipeline(ABC):
    @final
    def run(self, csv_text):              # テンプレートメソッド：手順を固定
        rows = [r for r in self.load(csv_text) if self.is_valid(r)]
        return self.render(rows)

    @abstractmethod
    def load(self, csv_text): ...         # 抽象メソッド：実装必須

    @abstractmethod
    def render(self, rows): ...

    def is_valid(self, row):              # フックメソッド：既定の実装を持つ
        return bool(row.get("name"))

class CsvReport(ReportPipeline):
    def load(self, csv_text):
        return list(csv.DictReader(io.StringIO(csv_text)))

    def render(self, rows):
        return " / ".join(r["name"] for r in rows)

class DevOnlyReport(CsvReport):
    def is_valid(self, row):              # フックだけ差し替える
        return bool(row.get("name")) and row.get("dept") == "dev"

class Broken(CsvReport):
    def run(self, csv_text):              # @finalを無視して上書き
        return "skipped"

src = "name,dept\nsato,dev\n,sales\nkato,sales\n"
print(CsvReport().run(src))
print(DevOnlyReport().run(src))
print(Broken().run(src))
ReportPipeline()
```

Python 3.14.6での実行結果です。

```
sato / kato
sato
skipped
TypeError: Can't instantiate abstract class ReportPipeline without an implementation for abstract methods 'load', 'render'
```

実装漏れは`@abstractmethod`がインスタンス化の時点で`TypeError`として落とします。一方、`@final`を付けた`run`を上書きした`Broken`は、例外も出さず上書き後の実装で動きました。[PEP 591](https://peps.python.org/pep-0591/)は実行時に継承や上書きを禁止する案を検討したうえで「Nothing else in typing does any runtime enforcement, though, so final will not either.」と述べています。Pythonで上書きを検出するには、mypyなどの静的型チェックをCIに組み込む方法があります。実行時にも禁止したい場合は、別途その制約を実装する必要があります。

## Spring Bootでテンプレートメソッドを組む：DIと抽象クラス

### @ComponentによるサブクラスのBean登録とMapへの注入

Spring Bootでも組み方は素のJavaと同じで、抽象クラスに手順を書き、サブクラスを`@Component`としてBean登録します。違いは利用側で、`Map<String, ImportJob>`を注入すると、同じ抽象型のBeanがBean名をキーにして全部渡されます。種類を増やすときはサブクラスを1つ足すだけで、利用側の分岐は増えません。次はSpring Boot 4.1.2（Spring Framework 7.0.10）とJDK 25で実行した検証アプリの抜粋です。検証ではspring-boot-starter-jdbcとH2を依存関係に追加し、自動構成されるDataSourceとトランザクションマネージャーを使用しています。以下ではimport文、起動クラス、メッセージを標準出力するAuditLogのBean定義を省略しています。publicクラスは別ファイルに置き、runnerメソッドは設定クラス内に定義します。saveは空実装なので、確認対象はDBへの保存ではなくトランザクションの有効状態です。

```
public abstract class ImportJob {
    protected final AuditLog audit;
    protected ImportJob(AuditLog audit) { this.audit = audit; }

    @Transactional
    public void execute(String src) {                 // テンプレートメソッド（finalにしない）
        List<String> rows = parse(src);
        rows.forEach(this::save);
        audit.record(name() + " tx=" + TransactionSynchronizationManager.isActualTransactionActive()
            + " rows=" + rows.size());
    }
    protected abstract List<String> parse(String src);
    protected abstract void save(String row);
    protected String name() { return getClass().getSimpleName(); }
}

@Component
public class CsvImportJob extends ImportJob {
    public CsvImportJob(AuditLog audit) { super(audit); }
    @Override protected List<String> parse(String src) { return List.of(src.split(",")); }
    @Override protected void save(String row) { }
}

// 利用側：同じ抽象型のBeanをBean名つきで受け取る
@Bean
CommandLineRunner runner(Map<String, ImportJob> jobs) {
    return args -> {
        System.out.println("beans=" + jobs.keySet());
        jobs.get("csvImportJob").execute("a,b,c");
    };
}
```

```
beans=[csvImportJob]
audit: CsvImportJob tx=true rows=3
```

抽象クラスに付けた`@Transactional`はサブクラスのBeanにも効き、手順全体が1つのトランザクションで動いています（`tx=true`）。コンストラクタ注入した`AuditLog`もサブクラス経由で親クラスに渡っています。

### CGLIBプロキシにおけるfinalの制約とNPEの再現条件

今回のようにCGLIBプロキシ経由でトランザクションを適用する構成では、対象の手順メソッドを`final`にすると、そのメソッドにアドバイスを適用できません。`@Transactional public final void execute(...)`を持つBeanを同じ環境で起動すると、まず起動時に次の警告が出ました。

```
WARN ... o.s.aop.framework.CglibAopProxy : Public final method [public final void demo.FinalJob.execute(java.lang.String)] cannot get proxied via CGLIB, consider removing the final marker or using interface-based JDK proxies.
```

そのまま`execute`を呼ぶと、トランザクションは張られず、コンストラクタで注入したはずのフィールドが`null`でした。

```
final: class=FinalJob$$SpringCGLIB$$0 audit=null tx=false
java.lang.NullPointerException: Cannot invoke "demo.AuditLog.record(String)" because "this.audit" is null
```

理由はSpringのプロキシの作り方にあります。[Spring Frameworkリファレンスのプロキシの章](https://docs.spring.io/spring-framework/reference/core/aop/proxying.html)は、インタフェースを実装しないBeanには対象クラスのサブクラスとしてCGLIBプロキシを作るとし、その制約に「final methods cannot be advised, because they cannot be overridden.」を挙げています。Spring Bootでは`spring.aop.proxy-target-class`の既定値が`true`（4.1.2の構成メタデータで確認）なので、インタフェースを実装したBeanでもサブクラス方式のCGLIBプロキシが使われます。さらに同じ箇所に、CGLIBプロキシのインスタンスはObjenesisで作られコンストラクタが呼ばれないとあります。`final`メソッドはプロキシが上書きできないため、呼び出しは本物のBeanへ転送されず、コンストラクタを通っていないプロキシ自身の上で実行される。だから`audit`が`null`になります。

対処は2つです。`@Transactional`などアノテーションで振る舞いを足すメソッドは`final`にせず、上書きされないことはコードレビューと命名で担保する。どうしても`final`で守りたいなら、トランザクション境界をテンプレートメソッドの外（呼び出し側のサービス）に移し、テンプレートを持つクラスにはアスペクトが一切かからないようにします。

### ステップの自己呼び出しとプロキシ型AOPの制約

テンプレートメソッドは、各ステップを`this`経由で呼びます。Springのプロキシはこの自己呼び出しに割り込めません。リファレンスの同じ章は「self invocation via an explicit or implicit this reference will bypass the advice.」と明記しており、実際に手順メソッドから`@Transactional`付きのステップを呼んだところ、ステップ内のトランザクションは無効（`tx=false`）でした。

「保存ステップだけ`REQUIRES_NEW`で別トランザクションにしたい」といった要求は、ステップのメソッドにアノテーションを付けても実現できません。そのステップを別のBeanに切り出して注入するか、トランザクションをテンプレートメソッド単位で設計します。自己呼び出しとプロキシの関係は[AOP（アスペクト指向プログラミング）とは？Spring Bootでの使い方・実行順序と自己呼び出しの落とし穴](/tech/details/3422/)でも詳しく扱っています。

## フレームワークの実例：OncePerRequestFilter・InputStream・unittest

### OncePerRequestFilter：Servletフィルターにおける手順と拡張点

Spring Bootで認証やログ用のフィルターを書くとき継承する`OncePerRequestFilter`は、継承型のテンプレートメソッドそのものです。Spring Framework 7.0.10の[ソースコード](https://github.com/spring-projects/spring-framework/blob/v7.0.10/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java)では、`doFilter`が手順を持ちます。HTTPリクエストかを確かめ、`shouldNotFilter`が`true`なら素通しし、同じリクエストで処理済みの印があれば二重実行を避け、印を付けてから`doFilterInternal`を呼び、最後に印を外す。開発者が書くのは`protected abstract`の`doFilterInternal`だけで、除外条件を足したいときに既定で`false`を返すフックの`shouldNotFilter`を上書きします。

ひとつ注意があります。7.0.10の`doFilter`には`final`が付いていません（手元の6.2.17〜7.0.10のjarを`javap`で確認）。上書きは言語上は可能ですが、既定の処理を置き換えると重複実行を防ぐ仕組みを損なうおそれがあります。また、実行回数の保証にはディスパッチとスレッドの条件があり、非同期ディスパッチなどを含むリクエスト全体で常に1回とは限りません。サブクラスでは`doFilter`に触れず、`doFilterInternal`と`shouldNotFilter`だけを書いてください。

### java.io.InputStream：read()を1本書けば動く代償

Javaの`java.io.InputStream`も典型例です。[Java SE 25のJavadoc](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/InputStream.html)によれば、引数なしの`read()`が抽象メソッドで、サブクラスはこれだけを実装すれば`read(byte[], int, int)`なども含めてストリームとして機能します。

代償も同じJavadocに書かれています。`read(byte[] b, int off, int len)`の説明は「The read(b, off, len) method for class InputStream simply calls the method read() repeatedly.」で、続けて「Subclasses are encouraged to provide a more efficient implementation of this method.」と促しています。`read()`だけを実装すると、配列への読み込みでも単一バイトの読み込みが繰り返されます。ただし、そのたびにシステムコールやネットワーク往復が発生するかは、サブクラスと下位層の実装によります。まとめ読みできるソースを扱うなら`read(byte[], int, int)`もオーバーライドし、利用側では`BufferedInputStream`で包みます。「1本実装すれば動く」手軽さの裏で、性能の責任はサブクラスの設計者に残ります。

### unittest.TestCase：setUpとtearDownはフックメソッド

Pythonの`unittest`では、テストの実行手順を`TestCase.run()`が固定し、前処理の`setUp`と後処理の`tearDown`が何もしない既定実装を持つフックとして開放されています。[公式ドキュメント](https://docs.python.org/3/library/unittest.html)は`tearDown`について「If setUp() succeeded, tearDown() will be run whether the test method succeeded or not.」と説明しています。裏返せば、`setUp`が例外を投げると`tearDown`は呼ばれません。`setUp`で複数のリソースを順に確保するなら、確保のたびに`addCleanup`で解放処理を登録しておくと取り残しを防げます。フックは「必ず呼ばれる」とは限らず、いつ呼ばれるかはテンプレートメソッド側の仕様で決まります。

## JdbcTemplateとの違い：コールバックによる可変部の受け渡し

クラス名にTemplateと付くため、SpringのJdbcTemplateをテンプレートメソッドパターンの実例として挙げる解説が目立ちます。公式ドキュメントの記述に照らすと、この対応づけは厳密ではありません。

[Spring Frameworkリファレンスのデータアクセス章](https://docs.spring.io/spring-framework/reference/data-access/jdbc/core.html)は、JdbcTemplateが「handles the creation and release of resources」し、「performs the basic tasks of the core JDBC workflow (such as statement creation and execution), leaving application code to provide SQL and extract results」と説明します。固定手順をフレームワークが持ち、可変部をアプリケーションが埋める。ここまでは同じ発想です。

違うのは埋め方です。同じ章は「you need only to implement callback interfaces, giving them a clearly defined contract」と述べ、`PreparedStatementCreator`や`RowCallbackHandler`といったコールバックインタフェースを挙げています。可変部を担うのはJdbcTemplateを継承したサブクラスではなく、引数として渡されるオブジェクト（ラムダ）です。GoFのテンプレートメソッドは可変部をサブクラスに委ねる形なので、委譲で埋めるJdbcTemplateは厳密には当てはまりません。

| クラス                       | 可変部の受け取り方                        | GoFのテンプレートメソッドか |
| ------------------------- | -------------------------------- | --------------- |
| OncePerRequestFilter      | 継承してdoFilterInternalを実装          | 該当する            |
| AbstractRoutingDataSource | 継承してdetermineCurrentLookupKeyを実装 | 該当する            |
| JdbcTemplate              | コールバックを引数で渡す                     | 発想は同じ・差し込み方が違う  |

クラス名にTemplateとあるかどうかでは判断できません。拡張点が「継承して埋める抽象メソッド」か「引数で渡すコールバック」かを見てください。この違いは、次のStrategyとの使い分けにそのまま効いてきます。

## Strategy・Factory Methodとの関係：拡張方式の比較と生成処理での併用

### Strategyとの使い分けは可変点の数と切り替えのタイミング

| 観点         | テンプレートメソッド | Strategy    |
| ---------- | ---------- | ----------- |
| 可変部の埋め方    | 継承・オーバーライド | 委譲・オブジェクト注入 |
| 切り替えのタイミング | クラス定義時     | 実行時に差し替え可   |
| 組み合わせ数     | 可変点の数だけ乗算  | 可変点ごとに独立    |
| 手順の持ち主     | 親クラスに1本    | 呼び出し側       |

可変点が1つで、処理の順番そのものを共有したいならテンプレートメソッドが簡潔です。可変点が複数あり、それぞれを独立して組み合わせたい場合は、委譲による構成を優先します。読み込み形式3種と出力形式3種の全組み合わせを専用の具象サブクラスで表すなら9クラスになります。委譲で各形式を独立した部品にすれば、可変部の実装は3＋3の6クラスに分けられます。ここでは共通の親クラス、インタフェース、利用側のクラスを数えていません。委譲側の書き方は[Strategyパターンとは？if文の分岐を置き換える仕組みとPHP・Java・Python・TypeScriptの実装例](/tech/details/10265/)に実装例があります。

### 生成ステップにおけるFactory Methodとの併用

テンプレートメソッドの抽象メソッドのうち、「オブジェクトを生成して返す」ステップがFactory Methodです。たとえば手順の途中で`createParser()`を呼び、どのパーサーを作るかはサブクラスが決める形にすると、テンプレートメソッドの中にFactory Methodが入ります。2つのパターンは競合するものではなく、テンプレートメソッドが定めた手順の中で、生成するオブジェクトの選択をFactory Methodに任せられます。生成の分離そのものは[ファクトリパターンとは？Factory Method・Abstract Factoryの違いとJava・Python実装例](/tech/details/3658/)で解説しています。

## テンプレートメソッドパターンのデメリットと採用を見送るべき場面

このパターンは継承を前提にしており、継承の弱点をそのまま引き継ぎます。次のいずれかに当てはまるなら採用しないでください。

**手順そのものが変わる可能性がある場合。**固定されるのはステップの順番です。「あるサブクラスだけ検証を2回走らせたい」「別のサブクラスでは整形を飛ばしたい」という要求が出た時点で、親クラスに条件分岐とフックが増えていきます。フックを足し続けた親クラスは、どのサブクラスがどのフックを上書きしているのか追えなくなります。

**可変点が3つ以上ある場合。**3つの可変点にそれぞれ3つの選択肢があり、全組み合わせを専用の具象サブクラスで表すなら27クラスになります。迷わずStrategyやファクトリによる生成の分離に寄せてください。

**親クラスを自分たちで保守できない場合。**外部ライブラリの抽象クラスを継承する場合、公開された拡張契約を超えて内部の処理順や呼び出し回数に依存すると、更新時に互換性を失うおそれがあります。フックが呼ばれる順番や回数は公開APIとして明示されていないことが多く、マイナーバージョンアップで変わっても仕様違反とは言えません。外部の抽象クラスに依存するなら、自分たちのコードとの間にアダプタを1枚挟みます。

**Springで手順メソッドにAOPのアノテーションを付けたい場合。**前述のとおり`final`で手順を守る定石とプロキシが衝突し、ステップ単位のアノテーションも自己呼び出しで無効になります。ステップごとにトランザクションや非同期実行を変えたいなら、ステップを別Beanに分けた委譲の構成のほうが素直です。

手順が仕様として確定し、可変部の独立した組み合わせが不要で、親クラスを自分たちで保守できるなら、テンプレートメソッドは少ない構成要素で共通処理を集約できる選択肢です。継承1段で済み、追加のインタフェース定義も注入の配線も要りません。既存システムの共通処理をこうした設計で整理し直す案件は、[フルスクラッチ開発の支援](/service/system/fullscratch/)でも扱っています。

## よくある質問

### テンプレートメソッドパターンのメリットは？

共通の処理手順を親クラス1か所に集約できるため、手順の修正がすべてのサブクラスへ一度に反映され、サブクラスは差分だけを書けば済みます。Javaなら`final`で手順の上書きを、`abstract`で実装漏れをコンパイル時に止められ、Pythonでも`@abstractmethod`の未実装はインスタンス化の時点で`TypeError`になります。

### Spring Bootでテンプレートメソッドパターンは使えますか？

使えます。抽象クラスに手順を書き、サブクラスを`@Component`で登録し、利用側では`Map<String, 抽象型>`や`List<抽象型>`で注入します。ただし`@Transactional`などを付けたテンプレートメソッドを`final`にすると、CGLIBプロキシが割り込めず、そのメソッドのアノテーションによるトランザクション開始が行われません。本記事のSpring Boot 4.1.2による検証構成では、プロキシ側のフィールドも`null`になりました。手順の中から`this`経由で呼ぶステップに付けた`@Transactional`なども、自己呼び出しのためプロキシを通らず効きません。

### SpringのJdbcTemplateはテンプレートメソッドパターンですか？

厳密には異なります。Spring Frameworkリファレンスは、JdbcTemplateが定型のJDBC処理を担い、アプリケーション側は`RowCallbackHandler`などのコールバックインタフェースを実装すればよいと説明しています。可変部を渡すのは継承ではなく引数（委譲）です。Springで継承型のテンプレートメソッドに当たるのは`OncePerRequestFilter`や`AbstractRoutingDataSource`です。

### Strategyパターンとの違いは？

可変部の差し込み方が違います。テンプレートメソッドはサブクラスのオーバーライドで埋めるため、組み合わせはクラス定義時に決まります。Strategyは振る舞いをオブジェクトとして外から渡すため、実行時に差し替えられます。可変点が1つで手順を共有したいならテンプレートメソッド、可変点が複数あるか実行時に切り替えたいならStrategyを選んでください。

### デザインパターンは全部で何個ありますか？

GoFと呼ばれるGamma・Helm・Johnson・Vlissidesの書籍『Design Patterns: Elements of Reusable Object-Oriented Software』がカタログ化したのは23パターンで、出版社Pearson（InformIT）の書誌ページにも23と記載されています。生成・構造・振る舞いの3分類で、テンプレートメソッドは振る舞いに関するパターンに含まれます。

## 関連記事

- [GoFデザインパターン23種類の一覧｜3分類と見分け方をJavaコードで解説](/tech/details/2615/)
- [Strategyパターンとは？if文の分岐を置き換える仕組みとPHP・Java・Python・TypeScriptの実装例](/tech/details/10265/)
- [ファクトリパターンとは？Factory Method・Abstract Factoryの違いとJava・Python実装例](/tech/details/3658/)
- [AOP（アスペクト指向プログラミング）とは？Spring Bootでの使い方・実行順序と自己呼び出しの落とし穴](/tech/details/3422/)
- [IoCコンテナとは？制御の反転の仕組みとSpring IoCコンテナ・Beanのスコープとライフサイクルを動くコードで解説](/tech/details/3210/)

---

出典: [テンプレートメソッドパターンとは？Java・Python・Spring Bootの実装例とStrategyとの違い](<https://www.issoh.co.jp/tech/details/3420/>)（株式会社一創）
