テンプレートメソッドパターン
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 59/70。
テンプレートメソッドパターン(Template Method pattern)は、アルゴリズムの骨組みを基底クラスで定義し、全体の構造を変えることなく、特定のステップをサブクラスでオーバーライドできるようにする振る舞いに関するパターンです。基底クラスがワークフローを制御し、派生クラスが個々の部分をカスタマイズします。
このパターンは継承と抽象メソッドを使用します。基底クラスには、一連のステップを呼び出す「テンプレートメソッド」が含まれており、そのステップの一部は抽象メソッドであり、サブクラスで実装する必要があります:
public abstract class DataProcessor
{
// テンプレートメソッド - アルゴリズムの構造を定義します
public void Process()
{
ReadData();
ProcessData();
SaveData();
}
protected abstract void ReadData();
protected abstract void ProcessData();
protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}
public class CsvProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading CSV file");
protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}
public class JsonProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading JSON file");
protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
protected override void SaveData() => Console.WriteLine("Saving to cloud");
}各サブクラスが独自の実装を提供する一方で、ベースクラスは操作の順序を強制します。
DataProcessor csv = new CsvProcessor();
csv.Process();
// CSVファイルを読み込んでいます
// CSVデータを解析しています
// デフォルトの場所に保存しています
DataProcessor json = new JsonProcessor();
json.Process();
// JSONファイルを読み込んでいます
// JSONデータを解析しています
// クラウドに保存していますTemplate Method パターンは、複数のクラスが同じアルゴリズムの構造を共有しながら、特定のステップが異なる場合に最適です。共通のロジックを基底クラスに配置することでコードの再利用を促進し、オーバーライド可能なメソッドを通じて柔軟性を提供します。
チャレンジ
簡単Template Method パターンを使用して、レポート生成システムを構築しましょう。異なる種類のレポート(売上レポートや在庫レポートなど)は、データの収集、フォーマット、結果の出力という同じ全体構造を共有していますが、各レポートタイプでこれらのステップの処理方法が異なります。
コードを3つのファイルに分けて構成します:
ReportGenerator.cs:Reporting名前空間内に抽象クラスReportGeneratorを作成します。このベースクラスは、Generate()というテンプレートメソッドを通じてレポート生成アルゴリズムのスケルトンを定義します。このメソッドは、GatherData()、FormatReport()、OutputReport()の3つのステップを順番に呼び出す必要があります。GatherData()とFormatReport()は抽象メソッドにし(各レポートタイプでこれらを異なる方法で実装します)、OutputReport()はSending to default printerと出力するデフォルト実装を持つ仮想メソッドにします。ConcreteReports.cs:ReportGeneratorを継承する2つの具体的なレポートクラスを同じ名前空間内に作成します:SalesReport-GatherData()を実装してCollecting sales data from databaseと出力し、FormatReport()を実装してFormatting as sales summaryと出力します。InventoryReport-GatherData()を実装してScanning warehouse inventoryと出力し、FormatReport()を実装してFormatting as inventory listと出力し、さらにOutputReport()をオーバーライドしてSending to warehouse managerと出力します。
Program.cs: レポートのインスタンスを作成して生成することで、すべてをまとめます。テンプレートメソッドにより、各レポートは同じ3ステップのプロセスに従いますが、各ステップの具体的な動作はレポートタイプに基づいて変化します。
1つの入力を受け取ります:
- レポートタイプ:
salesまたはinventory
入力に基づいて適切なレポートジェネレータを作成し、その Generate() メソッドを呼び出してください。
例えば、入力が sales の場合、出力は以下のようになります:
Collecting sales data from database
Formatting as sales summary
Sending to default printer入力が inventory の場合、出力は以下のようになります:
Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse manager両方のレポートがまったく同じアルゴリズム構造(収集、フォーマット、出力)に従っていますが、各ステップの実装がカスタマイズされていることに注目してください。SalesReport はデフォルトの出力動作を使用し、InventoryReport はそれをオーバーライドしています。これは、Template Method パターンによって、どのステップのオーバーライドを必須にし、どのステップをオプションでカスタマイズ可能にするかを制御できることを示しています!
チートシート
Template Method パターンは、アルゴリズムのスケルトンを基底クラスで定義し、全体の構造を変えることなく、特定のステップをサブクラスでオーバーライドできるようにします。
基底クラスには、一連のステップを呼び出す「テンプレートメソッド」が含まれています。一部のステップは抽象(サブクラスで実装が必須)ですが、他のステップは仮想(オーバーライドは任意)にすることができます:
public abstract class DataProcessor
{
// テンプレートメソッド - アルゴリズムの構造を定義します
public void Process()
{
ReadData();
ProcessData();
SaveData();
}
protected abstract void ReadData();
protected abstract void ProcessData();
protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}サブクラスは独自の実装を提供しますが、基底クラスが操作の順序を強制します:
public class CsvProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading CSV file");
protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}
public class JsonProcessor : DataProcessor
{
protected override void ReadData() => Console.WriteLine("Reading JSON file");
protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
protected override void SaveData() => Console.WriteLine("Saving to cloud");
}使用例:
DataProcessor csv = new CsvProcessor();
csv.Process();
// Reading CSV file
// Parsing CSV data
// Saving to default location
DataProcessor json = new JsonProcessor();
json.Process();
// Reading JSON file
// Parsing JSON data
// Saving to cloudTemplate Method パターンは、複数のクラスが同じアルゴリズム構造を共有しているが、特定のステップが異なる場合に最適です。共通のロジックを基底クラスに配置することでコードの再利用を促進し、オーバーライド可能なメソッドを通じて柔軟性を提供します。
自分で試してみよう
using System;
using Reporting;
class Program
{
public static void Main(string[] args)
{
string reportType = Console.ReadLine();
// TODO: reportTypeに基づいて適切なレポートジェネレータを作成する
// reportTypeが "sales" の場合は SalesReport を作成する
// reportTypeが "inventory" の場合は InventoryReport を作成する
// その後、レポートの Generate() メソッドを呼び出す
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。