テンプレートメソッドパターン
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 59/70。
Template Methodパターンは、base classでalgorithmの骨格を定義し、全体の構造を変更せずにsubclassが特定の手順をoverrideできるようにする振る舞いパターンです。base classがワークフローを制御し、derived classが個々の部分をカスタマイズします。
このパターンは継承とabstract methodを使用します。base classには、ステップのシーケンスを呼び出す「template method」が含まれており、その一部はabstractで、subclassによって実装されなければなりません。
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");
}各サブクラスは独自の implementation を提供し、base class は操作の order を強制します:
DataProcessor csv = new CsvProcessor();
csv.Process();
// CSVファイルの読み込み
// CSVデータの解析
// Saving to default location
DataProcessor json = new JsonProcessor();
json.Process();
// JSONファイルの読み込み
// JSONデータの解析
// クラウドへの保存Template Method パターンは、複数のクラスが同じ algorithm 構造を共有しながら、特定のステップが異なる場合に最適です。共通ロジックを base クラスに配置しつつ、オーバーライド可能な method によって柔軟性を実現することで、コードの再利用を促進します。
チャレンジ
簡単Template Method パターンを使用して report 生成システムを構築しましょう。異なる種類の report(sales report や inventory report など)は、全体的な構造を共有しています。つまり、どの report も data を GatherData し、FormatReport して、結果を OutputReport する必要がありますが、各 report の種類によってこれらの手順の処理方法は異なります。
コードを3つのファイルに分けて整理します。
ReportGenerator.cs:Reportingnamespace に abstract なReportGeneratorclass を Create します。この base class は、Generate()という template method を通じて report 生成 algorithm の骨格を定義します。この method は、GatherData()、FormatReport()、OutputReport()の3つの手順をこの order で call する必要があります。GatherData()とFormatReport()は abstract にし(各 report type が異なる方法で implementation します)、OutputReport()は、Sending to default printerを print する default implementation を持つ virtual method にします。ConcreteReports.cs: 同じ namespace に、ReportGeneratorを継承する2つの concrete report class を Create します。SalesReport-GatherData()を実装してCollecting sales data from databaseを print し、FormatReport()を実装してFormatting as sales summaryを print しますInventoryReport-GatherData()を実装してScanning warehouse inventoryを print し、FormatReport()を実装してFormatting as inventory listを print し、OutputReport()を override してSending to warehouse managerを print します
Program.cs: report instance を Create して Generate することで、すべてを結び付けます。template method により、各 report は同じ3段階の Process に従いますが、各手順の具体的な動作は report type に応じて異なります。
1つの input を受け取ります。
- report type:
salesまたはinventory
input に基づいて appropriate な report generator を Create し、その Generate() method を call します。
たとえば、input が sales の場合、Output は次のようになります。
Collecting sales data from database
Formatting as sales summary
Sending to default printerinput が inventory の場合、Output は次のようになります。
Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse managerどちらの report もまったく同じ algorithm structure(GatherData、FormatReport、OutputReport)に従っていますが、各手順の implementation はカスタマイズされていることに注目してください。SalesReport は default の Output 動作を使用する一方、InventoryReport はそれを override します。これは、Template Method パターンによって、どの手順を必ず override する必要があり、どの手順を任意でカスタマイズできるかを制御できることを示しています。
自分で試してみよう
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() メソッドを呼び出す
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: C#オンラインコンパイラ