模板方法模式
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 94 课,共 107 课。
模板方法模式在基类中定义算法的骨架,让子类可以重写特定步骤,而不改变整体结构。装饰器通过包装对象来添加行为,而模板方法则控制算法的流程,并允许自定义各个步骤。
在 Go 中,由于没有继承,我们使用结构体嵌入结合接口来实现这种模式。“模板”结构体定义算法的结构,并调用可自定义的方法:
type DataProcessor interface {
ReadData() string
ProcessData(data string) string
SaveData(result string) string
}
type BaseProcessor struct {
Impl DataProcessor
}
func (b *BaseProcessor) Execute() string {
data := b.Impl.ReadData()
result := b.Impl.ProcessData(data)
return b.Impl.SaveData(result)
}具体实现为每个步骤提供自己的版本,同时算法流程保持不变:
type CSVProcessor struct{}
func (c CSVProcessor) ReadData() string { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string { return "Saved: " + r }
type JSONProcessor struct{}
func (j JSONProcessor) ReadData() string { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string { return "Stored: " + r }模板方法 Execute 按固定顺序编排各个步骤:
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // 已保存:processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // 已存储:parsed-json-data当算法具有相同的结构,但具体步骤有所不同时,模板方法模式就很适合使用,例如数据导入/导出流程、报告生成器或测试框架。
挑战
简单让我们使用模板方法模式来构建一个报告生成系统!你将创建一个框架,让不同类型的报告遵循相同的生成流程:收集数据、设置格式并输出结果,但每种报告类型都可以用不同的方式自定义这些步骤。
你将把代码分成三个文件:
report.go:定义接口和负责协调报告生成算法的基础处理器。创建一个
ReportGenerator接口,其中包含代表报告生成步骤的三个方法:GatherData() string:获取报告的原始数据FormatData(data string) string:将数据转换为报告格式OutputReport(formatted string) string:生成最终输出消息
构建一个持有
ReportGenerator实现的ReportProcessor结构体。添加一个Generate() string方法,按顺序执行三个步骤:先收集数据,再设置格式,最后输出,并返回最终结果。generators.go:实现具体的报告生成器,自定义每个步骤。创建两种报告类型:
SalesReport,包含一个Region字段(字符串)GatherData()返回sales-data-[region]FormatData(data)返回SALES REPORT: [data]OutputReport(formatted)返回Printed: [formatted]
InventoryReport,包含一个Warehouse字段(字符串)GatherData()返回inventory-[warehouse]FormatData(data)返回*** [data] ***OutputReport(formatted)返回Exported: [formatted]
main.go:演示相同的算法结构如何根据具体实现生成不同的结果。读取报告类型(
sales或inventory)和配置值(销售报告对应区域,库存报告对应仓库)。创建相应的生成器,将其封装在ReportProcessor中,调用Generate(),并打印结果。
输入如下:
- 第 1 行:报告类型(
sales或inventory) - 第 2 行:配置值(区域名称或仓库名称)
例如,给定:
sales
North你的输出应为:
Printed: SALES REPORT: sales-data-North再例如,给定:
inventory
MainHub你的输出应为:
Exported: *** inventory-MainHub ***再例如,给定:
sales
West你的输出应为:
Printed: SALES REPORT: sales-data-West注意,ReportProcessor 始终按相同顺序调用相同的三个步骤,但每种报告类型都会为这些步骤提供自己的实现。算法的框架保持不变,具体细节则有所不同:这就是模板方法模式!
亲手试试
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 读取报表类型
scanner.Scan()
reportType := scanner.Text()
// 读取配置值(区域或仓库)
scanner.Scan()
configValue := scanner.Text()
// TODO: 根据 reportType 创建合适的生成器
// - 如果 reportType 是 "sales",创建 SalesReport,并将 Region 设置为 configValue
// - 如果 reportType 是 "inventory",创建 InventoryReport,并将 Warehouse 设置为 configValue
// TODO: 使用生成器创建 ReportProcessor
// TODO: 调用 Generate() 并打印结果
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器