Menu
Coddy logo textTech

テンプレートメソッドパターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 73/87。

テンプレートメソッドパターンは、base classでalgorithmの骨格を定義し、全体の構造を変更せずにサブクラスが特定の手順をオーバーライドできるようにする振る舞いに関するデザインパターンです。レシピのようなものだと考えてください。一般的な調理プロセスは同じままですが、個々の材料や技法は変えることができます。

このパターンでは、一連のステップを呼び出すテンプレートメソッドを持つabstract classを使用します。一部のステップにはデフォルト実装がありますが、その他はabstractであり、サブクラスで実装する必要があります。

abstract class DataProcessor {
    // テンプレートメソッド - アルゴリズムの構造を定義する
    public final void process() {
        readData();
        processData();
        saveData();
    }
    
    abstract void readData();
    abstract void processData();
    
    // デフォルト実装(フックメソッド)
    void saveData() {
        System.out.println("Saving to default location");
    }
}

class CSVProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading CSV file");
    }
    
    void processData() {
        System.out.println("Parsing CSV data");
    }
}

class XMLProcessor extends DataProcessor {
    void readData() {
        System.out.println("Reading XML file");
    }
    
    void processData() {
        System.out.println("Parsing XML data");
    }
}

テンプレートメソッドには通常、サブクラスがアルゴリズムの構造を変更できないように final が付けられます。呼び出されると、すべてのサブクラスに対して同じ手順を実行します。

DataProcessor csv = new CSVProcessor();
csv.process();
// 出力:
// CSVファイルを読み込み中
// CSVデータを解析中
// デフォルトの場所に保存中

Template Method Patternは、同じ構造を共有しながら特定のステップが異なるアルゴリズムがある場合に最適です。共通のロジックをbaseクラスに配置し、継承によるカスタマイズを可能にすることで、コードの再利用を促進します。

challenge icon

チャレンジ

簡単

Template Method Pattern を使ってドキュメント生成システムを構築しましょう!レポートや請求書など、異なるドキュメントの種類が同じ生成プロセスに従いながら、特定の手順をカスタマイズできるフレームワークを作成します。全体のワークフローは固定されているものの、詳細がドキュメントの種類ごとに異なるシナリオに最適です。

コードを4つのファイルに分けて構成します。

  • DocumentGenerator.java:テンプレートメソッドを定義する抽象基底クラスを作成します。DocumentGeneratorには次の要素を含めます。

    3つの手順、createHeader()createBody()createFooter()を順番に呼び出してアルゴリズムの骨格を定義する、generateDocument()という名前のfinalメソッド。

    サブクラスが実装する必要がある2つの抽象メソッド、createHeader()createBody()

    --- End of Document ---を出力するデフォルト実装を持つフックメソッドcreateFooter()

  • ReportGenerator.java:レポートを生成する具象サブクラスを作成します。コンストラクターを通じてレポートのタイトル(String)を受け取ります。

    createHeader()メソッドは=== REPORT: [title] ===を出力します。

    createBody()メソッドはReport content goes here...を出力します。

    継承したデフォルトのフッターを使用します。

  • InvoiceGenerator.java:請求書を生成する別の具象サブクラスを作成します。コンストラクターを通じて顧客名(String)と金額(double)を受け取ります。

    createHeader()メソッドは*** INVOICE ***を出力します。

    createBody()メソッドは、1行目にCustomer: [name]を出力し、次の行にAmount Due: $[amount]を出力します(金額は小数点以下2桁の形式にします)。

    createFooter()をオーバーライドしてThank you for your business!を出力します。

  • Main.java:テンプレートメソッドシステムを組み合わせます!3つの入力、つまりレポートのタイトル(String)、顧客名(String)、請求金額(double)を受け取ります。

    タイトルを指定してReportGeneratorを作成し、generateDocument()を呼び出します。区切りとして空行を出力します。次に、顧客名と金額を指定してInvoiceGeneratorを作成し、generateDocument()を呼び出します。

入力は、レポートのタイトル(String)、顧客名(String)、請求金額(double)の順に3つ受け取ります。

たとえば、入力がQ4 SalesAcme Corp1500.00の場合、出力は次のようになります。

=== REPORT: Q4 Sales ===
Report content goes here...
--- End of Document ---

*** INVOICE ***
Customer: Acme Corp
Amount Due: $1500.00
Thank you for your business!

どちらのドキュメントの種類も、テンプレートメソッドで定義された同じ3段階のプロセスに従っていますが、それぞれの手順を異なる方法でカスタマイズしていることに注目してください。ReportGeneratorはデフォルトのフッターを使用する一方、InvoiceGeneratorは独自のフッターを提供します。これがTemplate Method Patternにおけるフックメソッドの力です!

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み取る
        String reportTitle = scanner.nextLine();
        String customerName = scanner.nextLine();
        double invoiceAmount = scanner.nextDouble();
        
        // TODO: タイトルを指定してReportGeneratorを作成し、generateDocument()を呼び出す
        
        // TODO: 区切りのために空行を出力する
        
        // TODO: 顧客名と金額を指定してInvoiceGeneratorを作成し、generateDocument()を呼び出す
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ