🎃
デザインパターンを学ぶ #18 テンプレートメソッド(Template Method)
1. はじめに
今回は テンプレートメソッド(Template Method)パターン。
目的は、処理の流れを抽象クラスで固定しつつ、一部の手順だけをサブクラスに任せることです。
フレームワークのライフサイクルやテストコードの setUp/tearDown などでもよく使われます。
2. テンプレートメソッドとは?
アルゴリズムの骨格(流れ)を抽象クラスに定めておき、具体的な実装部分をサブクラスに委ねる仕組みです。
登場役は以下のとおりです:
- AbstractClass … テンプレートメソッドを定義(処理の流れを固定)
- ConcreteClass … 差し替えポイントを具体的に実装
これにより「流れは必ず守られるが、中身は差し替えられる」設計が可能になります。
3. 実装イメージ(PHP)
<?php
abstract class DataImporter {
// テンプレートメソッド(流れを固定)
public final function run(string $path): void {
$rows = $this->load($path);
$this->validate($rows);
$this->save($rows);
$this->after();
}
protected function load(string $path): array {
return file($path, FILE_IGNORE_NEW_LINES);
}
protected function save(array $rows): void {
echo "Saved " . count($rows) . " rows\n";
}
// 差し替えポイント
abstract protected function validate(array $rows): void;
// フックメソッド(任意)
protected function after(): void {}
}
class UserImporter extends DataImporter {
protected function validate(array $rows): void {
foreach ($rows as $i => $row) {
if (!str_contains($row, '@')) {
throw new InvalidArgumentException("Row {$i} invalid email");
}
}
}
}
class ProductImporter extends DataImporter {
protected function validate(array $rows): void {
foreach ($rows as $i => $row) {
if (!preg_match('/^SKU-/', $row)) {
throw new InvalidArgumentException("Row {$i} invalid sku");
}
}
}
}
// 実行例
(new UserImporter())->run('users.txt');
(new ProductImporter())->run('products.txt');
4. メリット・デメリット
メリット
- アルゴリズムの流れを強制できる
- 重複する処理を抽象クラスにまとめられる
- 差し替えポイントを明確化できる
デメリット
- 継承ベースなので柔軟性は委譲(Strategy等)に劣る
- 抽象クラスが肥大化しやすい
- ランタイムに手順を変えるのは難しい
5. 使いどころ
- バッチ処理やインポート処理の共通フローを統一したいとき
- フレームワークのライフサイクルフック(before/after)
- テストの共通処理(setUp/tearDown)
「流れは固定、中身は差し替え」が欲しいときに使えるのがテンプレートメソッドです。
Discussion