模板方法模式
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 78 课,共 91 课。
模板方法模式是一种行为型设计模式,它在基类中定义算法的框架,让子类可以重写特定步骤,而不改变整体结构。基类控制工作流程,子类则自定义各个部分。
考虑构建不同类型的文档。
每份文档都遵循相同的流程:创建页眉,添加内容,创建页脚。但每个步骤的具体实现会因文档类型而异。模板方法定义了这个顺序,而子类负责实现细节。
<?php
abstract class DocumentGenerator {
// 模板方法:定义算法结构
public final function generate(): string {
$output = $this->createHeader();
$output .= $this->createContent();
$output .= $this->createFooter();
return $output;
}
// 由子类实现的步骤
abstract protected function createHeader(): string;
abstract protected function createContent(): string;
abstract protected function createFooter(): string;
}
class HtmlDocument extends DocumentGenerator {
protected function createHeader(): string {
return "<html><head></head><body>";
}
protected function createContent(): string {
return "<p>Hello World</p>";
}
protected function createFooter(): string {
return "</body></html>";
}
}
class TextDocument extends DocumentGenerator {
protected function createHeader(): string {
return "=== DOCUMENT ===\n";
}
protected function createContent(): string {
return "Hello World\n";
}
protected function createFooter(): string {
return "=== END ===";
}
}
$html = new HtmlDocument();
echo $html->generate() . "\n";
$text = new TextDocument();
echo $text->generate();
输出:
<html><head></head><body><p>Hello World</p></body></html>
=== DOCUMENT ===
Hello World
=== END ===注意模板方法上的 final 关键字。它可以防止子类更改算法的结构。如果有多个类遵循相同的工作流程,但实现细节不同,那么这种模式就很适合你。添加一种新的文档类型,意味着创建一个新的子类,而无需修改现有代码。
挑战
简单让我们使用模板方法模式构建一个报告生成系统。不同类型的报告遵循相同的结构,包括标题部分、数据部分和摘要部分,但每种报告类型对这些部分的格式处理方式不同。模板方法将定义此工作流程,而子类可以自定义每个步骤。
你将把代码分成 4 个文件:
ReportGenerator.php:创建一个抽象的ReportGenerator类,用于定义报告生成算法的框架。你的类应当:- 在构造函数中接收报告标题(使用构造函数属性提升)
- 包含一个
final公共方法generate(): string,按顺序调用 3 个步骤:createTitle()、createBody()和createSummary(),并拼接它们的结果 - 声明 3 个抽象受保护方法,供子类实现:
createTitle(): string、createBody(): string和createSummary(): string
HtmlReport.php:引入 ReportGenerator 文件,并创建一个继承它的HtmlReport类。此报告使用 HTML 格式输出:createTitle()返回"<h1>[title]</h1>"createBody()返回"<p>Report data goes here</p>"createSummary()返回"<footer>End of report</footer>"
PlainTextReport.php:引入 ReportGenerator 文件,并创建一个继承它的PlainTextReport类。此报告使用简单的文本格式:createTitle()返回"### [title] ###\n"createBody()返回"Report data goes here\n"createSummary()返回"--- End of report ---"
main.php:引入这两个报告文件。你将收到两个输入:报告类型("html"或"text")和报告标题。根据报告类型,使用给定的标题创建相应的报告生成器。调用
generate()并打印完整报告。
模板方法模式确保所有报告都遵循相同的三步结构,同时允许每种报告类型以不同方式设置各部分的格式。generate() 上的 final 关键字可防止子类更改算法的执行顺序。子类只能自定义各个步骤。
亲手试试
<?php
require_once 'HtmlReport.php';
require_once 'PlainTextReport.php';
// 读取输入
$reportType = trim(fgets(STDIN));
$reportTitle = trim(fgets(STDIN));
// TODO:根据报告类型("html" 或 "text"),
// 使用给定的标题创建相应的报告生成器
// TODO:调用 generate() 并打印完整的报告
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器