※最近デザインパターンを勉強し直そうと思って読んだこの書籍を自分なりにまとめていっている記事です。
※この記事ではTemplate Methodパターンについて書いています。
ソフトウェア開発において、類似した手順を持つ処理をいくつも抱えている場合、どのようにしてコードの一貫性と再利用性を確保しますか?例えば、複数の種類のレポートを生成するアプリケーションや、異なる種類のデータを処理するウェブアプリケーション。これらのシナリオでは、共通の処理フローを確保しながら、特定の部分でカスタマイズが必要になります。Template Methodパターンは、こうした問題を解決するための有用なデザインパターンです。
Template Methodパターンとは?
Template Methodパターンは、スーパークラスにアルゴリズムの骨組みを定義し、サブクラスでその具体的なステップを実装するデザインパターンです。これにより、アルゴリズム全体の構造を変えずに、サブクラスで部分的に処理を変更できます。
よくあるシナリオ
- レポート生成システム: 共通のレポート生成手順を持ちつつ、データソースやフォーマットが異なる場合
- データ処理パイプライン: データの前処理、変換、後処理などのステップが共通しているが、具体的な変換ロジックが異なる場合
- ゲーム開発: 共通のゲームループを持ちつつ、異なるゲーム要素を処理する場合
Template Methodパターンの利点
- コードの再利用: アルゴリズムの共通部分をスーパークラスにまとめ、サブクラスでカスタマイズできるため、コードの再利用性が高まります
- 一貫性の保持: アルゴリズムの流れがスーパークラスで固定されるため、どのサブクラスを使っても一貫した動作が保証されます
Template Methodパターンの構造
スーパークラス
|--------------------------|
| + templateMethod(): void |
| + step1(): void { ... } |
| + step2(): void { ... } |
| - step3(): void; |
|--------------------------|
/|\
|
サブクラスA サブクラスB
|--------------------| |--------------------|
| + step3(): void { ... } | + step3(): void { ... } |
|--------------------| |--------------------|
この図では、スーパークラスがアルゴリズムのフレームワークを定義し、サブクラスが特定のステップを実装しています。
Javaでの実装例
ここでは、料理の手順を例にしてTemplate Methodパターンを実装します。Cooking クラスをスーパークラスとし、JapaneseCooking と ItalianCooking クラスをサブクラスとして具体的な料理の手順を定義します。
1. スーパークラス (Cooking)
abstract class Cooking {
// テンプレートメソッド: 全体の流れを定義
public final void cookDish() {
prepareIngredients(); // 共通の準備ステップ
cookMain(); // サブクラスが定義するメイン料理の調理ステップ
serveDish(); // 共通の提供ステップ
}
// 共通のステップ: 材料の準備
void prepareIngredients() {
System.out.println("Preparing ingredients.");
}
// 抽象メソッド: サブクラスで具体的に実装
abstract void cookMain();
// 共通のステップ: 料理の提供
void serveDish() {
System.out.println("Serving the dish.");
}
}
Cooking クラスでは、料理の基本的な手順を cookDish テンプレートメソッドで定義しています。共通の準備と提供のステップはスーパークラスで実装され、メインの料理部分はサブクラスで実装されます。
2. サブクラス (JapaneseCooking, ItalianCooking)
class JapaneseCooking extends Cooking {
@Override
void cookMain() {
System.out.println("Cooking sushi.");
}
}
class ItalianCooking extends Cooking {
@Override
void cookMain() {
System.out.println("Cooking pasta.");
}
}
JapaneseCooking クラスでは cookMain メソッドで寿司の調理を、ItalianCooking クラスではパスタの調理をそれぞれ実装しています。これにより、共通のフレームワークの中で異なる料理が作成されます。
3. Template Methodパターンの使用例
public class TemplateMethodExample {
public static void main(String[] args) {
Cooking japanese = new JapaneseCooking();
japanese.cookDish(); // 日本料理の調理
Cooking italian = new ItalianCooking();
italian.cookDish(); // イタリア料理の調理
}
}
このコードを実行すると、それぞれのサブクラスによる具体的な調理手順が統一されたテンプレートの中で実行されます。
ユースケース例
-
ウェブアプリケーションでの使用例
ウェブアプリケーションにおいて、複数のデータフォーマットを扱う必要がある場合にTemplate Methodパターンを適用できます。共通の処理フロー(例: データのバリデーション、保存、レスポンス生成)をスーパークラスに定義し、各データフォーマットに特化した処理をサブクラスで実装します -
ゲーム開発での使用例
ゲーム開発では、共通のゲームループをTemplate Methodパターンで定義し、各ゲームの具体的なロジックをサブクラスで実装することができます。これにより、異なるゲームでも一貫したループ構造が保証されます
Template Methodパターンのベストプラクティス
- テンプレートメソッドをfinalなどで変更できないようにする: テンプレートメソッドをfinalにして、サブクラスがこのメソッドをオーバーライドしてアルゴリズム全体を変更できないようにします。これにより、アルゴリズムの一貫性が保たれます
- 共通処理はスーパークラスに集約する: サブクラス間で重複する処理はスーパークラスに移動し、コードの重複を最小限に抑えます。これにより、メンテナンス性が向上します
- 必要な部分だけを抽象化する: 抽象メソッドを増やしすぎると、サブクラスでの実装が複雑になりやすいです。必要最低限の部分のみを抽象メソッドとして定義し、他の部分は可能な限りスーパークラスで実装します
- コンストラクタや依存性注入を活用する: サブクラスで必要なオブジェクトやデータをスーパークラスに渡す際は、コンストラクタや依存性注入を活用して、コードの柔軟性を高めましょう
まとめ
Template Methodパターンは、アルゴリズムの共通部分をスーパークラスに定義し、サブクラスで特定の処理をカスタマイズすることで、コードの再利用性と保守性を高める非常に強力なデザインパターンです。特に、複数の処理が共通のフレームワーク内で行われる場合に、その効果は顕著に現れるので、ぜひ活用してみてください🔥