Menu
CoddyTech

模板方法模式

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 73 课,共 87 课。

模板方法模式是一种行为型设计模式,它在基类中定义算法的框架,允许子类重写特定步骤,而不改变整体结构。你可以把它想象成一道菜谱:整体烹饪流程保持不变,但具体的食材或烹饪技巧可以有所不同。

这种模式使用一个包含模板方法的抽象类,该方法会依次调用一系列步骤。有些步骤有默认实现,其他步骤则是抽象的,必须由子类实现:

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 数据
// 正在保存到默认位置

当多个算法具有相同的结构,但在某些具体步骤上有所不同时,模板方法模式就很适用。它将公共逻辑放在基类中,并通过继承支持自定义,从而促进代码复用。

challenge icon

挑战

简单

让我们使用模板方法模式来构建一个文档生成系统!你将创建一个框架,让不同类型的文档(报告和发票)遵循相同的生成流程,同时自定义特定步骤。这非常适合整体工作流程固定、但不同文档类型的细节各不相同的场景。

你将把代码分成 4 个文件:

  • DocumentGenerator.java:创建定义模板方法的抽象基类。你的 DocumentGenerator 应包含:

    一个名为 generateDocument() 的 final 方法,通过依次调用 3 个步骤来定义算法框架:createHeader()、createBody() 和 createFooter()。

    两个抽象方法:子类必须实现 createHeader() 和 createBody()。

    一个钩子方法 createFooter(),其默认实现会打印 --- End of Document ---。

  • ReportGenerator.java:创建一个用于生成报告的具体子类。它应通过构造函数接收报告标题(String)。

    createHeader() 方法会打印 === REPORT: [title] ===

    createBody() 方法会打印 Report content goes here...

    使用继承而来的默认页脚。

  • InvoiceGenerator.java:再创建一个用于生成发票的具体子类。它应通过构造函数接收客户姓名(String)和金额(double)。

    createHeader() 方法会打印 *** INVOICE ***

    createBody() 方法会先在一行打印 Customer: [name],然后在下一行打印 Amount Due: $[amount](将金额格式化为两位小数)。

    重写 createFooter(),使其打印 Thank you for your business!

  • Main.java:把模板方法系统组合起来!你将收到 3 个输入:报告标题(String)、客户姓名(String)和发票金额(double)。

    使用标题创建一个 ReportGenerator,并调用 generateDocument()。打印一个空行作为分隔。然后使用客户姓名和金额创建一个 InvoiceGenerator,并调用 generateDocument()。

你将按顺序收到 3 个输入:报告标题(String)、客户姓名(String)和发票金额(double)。

例如,输入为 Q4 Sales、Acme Corp 和 1500.00 时,输出如下:

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

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

注意,两种文档类型都遵循模板方法中定义的相同步骤,但各自以不同方式自定义这些步骤。ReportGenerator 使用默认页脚,而 InvoiceGenerator 提供自己的页脚。这就是模板方法模式中钩子方法的作用!

亲手试试

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 在线编译器