Template Methodパターン
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 99/104。
Template Method パターンは、base class で algorithm の骨格を定義し、subclass が algorithm 全体の構造を変更せずに特定のステップをオーバーライドできるようにします。algorithm 全体を入れ替える Strategy とは異なり、Template Method は algorithm を固定したまま、個々のステップをカスタマイズできるようにします。
base class はテンプレート method(algorithm)を実装し、サブクラスが提供する必要のある抽象または仮想 method を呼び出します:
#include <iostream>
class DataProcessor {
public:
// テンプレートメソッド - アルゴリズムの骨格を定義する
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // 実装必須
virtual void processData() = 0; // 実装必須
// フック - デフォルト動作付きのオプションのオーバーライド
virtual void saveResults() {
std::cout << "Saving to default location\n";
}
};
class CSVProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading CSV file\n";
}
void processData() override {
std::cout << "Parsing CSV data\n";
}
};
class JSONProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading JSON file\n";
}
void processData() override {
std::cout << "Parsing JSON data\n";
}
void saveResults() override {
std::cout << "Saving to cloud storage\n";
}
};
int main() {
CSVProcessor csv;
csv.process(); // デフォルトの saveResults を使用
JSONProcessor json;
json.process(); // カスタムの saveResults を使用
}process()メソッドはテンプレートメソッドです。固定された手順の順序を定義します。サブクラスはloadData()とprocessData()をそれぞれ異なる方法で実装しますが、順序が変わることはありません。saveResults()メソッドは「フック」です。デフォルトの実装が用意されており、サブクラスは必要に応じてオーバーライドできます。
不変のステップを持つアルゴリズムがありながら、特定の操作には柔軟性が必要な場合、または類似したクラス間でのコードの重複を避けたい場合は、Template Methodを使用します。
チャレンジ
簡単Template Method パターンを使用して、Report Generator システムを構築しましょう。売上レポートや在庫レポートなど、異なる種類のレポートが同じ生成プロセスに従いながら、各レポートの種類で特定のステップをカスタマイズできるフレームワークを作成します。これは Template Method に最適なシナリオです。全体のワークフローは固定されたまま、個々のステップだけが変化します。
コードを3つのファイルに分けて整理します。
ReportGenerator.h:テンプレートメソッドを持つ抽象基底クラスを定義します。レポート生成アルゴリズムの骨格を定義する
ReportGeneratorクラスを作成します。テンプレートメソッドgenerateReport()は、次のステップを順番に実行します。gatherData():純粋仮想関数で、サブクラスによって実装する必要がありますformatReport():純粋仮想関数で、サブクラスによって実装する必要がありますaddHeader():--- Report ---を出力するデフォルト動作を持つフックprintReport():純粋仮想関数で、サブクラスによって実装する必要があります
addHeader()メソッドは、サブクラスが必要に応じてオーバーライドし、ヘッダーをカスタマイズできるフックとして機能します。Reports.h:2つの具象レポートジェネレーターを実装します。次のような
SalesReportクラスを作成します。gatherData()はGathering sales data from databaseを出力しますformatReport()はFormatting sales figuresを出力しますprintReport()はSales Total: $[amount]を出力します。ここで amount はコンストラクターに渡されます
次のような
InventoryReportクラスを作成します。gatherData()はScanning inventory recordsを出力しますformatReport()はOrganizing inventory by categoryを出力しますaddHeader()はフックをオーバーライドして=== Inventory Report ===を出力しますprintReport()はItems in stock: [count]を出力します。ここで count はコンストラクターに渡されます
両方のクラスは、それぞれの値(amount または count)をコンストラクターで受け取る必要があります。
main.cpp:Template Method パターンを実演します。2つの入力を読み取ります。
- 売上金額(整数)
- 在庫数(整数)
売上金額を使用して
SalesReportを作成し、生成します。次に、在庫数を使用してInventoryReportを作成し、生成します。読みやすさのため、2つのレポートの間に空行を出力します。
たとえば、入力が 15000 と 250 の場合:
Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $15000
Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 250入力が 8500 と 120 の場合:
Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $8500
Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 120どちらのレポートも、generateReport() で定義されたまったく同じステップの順序に従っていますが、それぞれのクラスでステップを異なる方法で実装していることに注目してください。InventoryReport はフックメソッドをオーバーライドしてヘッダーをカスタマイズする例でもあり、一方 SalesReport はデフォルトを使用します。これが Template Method パターンの実際の動作です。アルゴリズムの構造は基底クラスで固定されますが、詳細は柔軟に変更できます。
自分で試してみよう
#include <iostream>
#include "Reports.h"
using namespace std;
int main() {
// 入力を読み取る
int salesAmount;
int inventoryCount;
cin >> salesAmount;
cin >> inventoryCount;
// TODO: salesAmountを使ってSalesReportを作成し、生成する
// TODO: レポートの間に空行を出力する
// TODO: Create an InventoryReport with inventoryCount and generate it
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
14デザインパターン パート2
CommandパターンAdapterパターンDecoratorパターンTemplate MethodパターンStateパターンCompositeパターンパターンとしてのRAII3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ