模板方法模式
本课属于 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 数据
// 正在保存到默认位置当多个算法具有相同的结构,但在某些具体步骤上有所不同时,模板方法模式就很适用。它将公共逻辑放在基类中,并通过继承支持自定义,从而促进代码复用。
挑战
简单让我们使用模板方法模式来构建一个文档生成系统!你将创建一个框架,让不同类型的文档(报告和发票)遵循相同的生成流程,同时自定义特定步骤。这非常适合整体工作流程固定、但不同文档类型的细节各不相同的场景。
你将把代码分成 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()
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器