模板方法模式 (Template Method Pattern)
意图
模板方法模式是一种行为型设计模式,它在一个方法中定义一个操作的算法骨架,将一些步骤延迟到子类中实现。模板方法模式使得子类可以重定义算法的某些特定步骤,而不改变算法的整体结构。这个模式用于让子类在不改变算法结构的情况下,重新定义算法的某些特定步骤。
问题
在现实世界中,考虑一个报表生成系统,生成报表的过程包括数据获取、数据处理和报表输出等多个步骤。不同类型的报表可能有不同的数据获取和处理方式,但整体流程是相同的。如果将整个报表生成流程都写在一个类中,会导致类变得复杂且难以维护。模板方法模式允许我们在基类中定义报表生成的算法骨架,并将具体的数据获取和处理步骤推迟到子类中实现,从而简化了报表生成流程的管理。
解决方案
使用模板方法模式,我们可以在基类中定义一个模板方法,该方法包含了算法的整体结构,并调用一些可以被子类重写的钩子方法。具体的步骤由子类实现。基类保证了算法的骨架不被改变,而子类则提供了具体的实现细节。
模式结构
- 抽象类(Abstract Class):定义模板方法,并实现模板方法中的部分步骤。声明钩子方法,子类可以重写这些方法以提供具体实现。
- 具体类(Concrete Class):继承抽象类,并实现抽象类中声明的钩子方法,提供具体的步骤实现。
代码
以下是使用Go语言实现的模板方法模式示例:
package main
import "fmt"
// 抽象类 - 报表生成
type ReportGenerator struct{}
// 模板方法 - 定义算法骨架
func (r *ReportGenerator) GenerateReport() {
r.FetchData()
r.ProcessData()
r.OutputReport()
}
// 钩子方法 - 数据获取,子类可以重写
func (r *ReportGenerator) FetchData() {
fmt.Println("获取数据")
}
// 钩子方法 - 数据处理,子类可以重写
func (r *ReportGenerator) ProcessData() {
fmt.Println("处理数据")
}
// 钩子方法 - 报表输出,子类可以重写
func (r *ReportGenerator) OutputReport() {
fmt.Println("输出报表")
}
// 具体类 - 销售报表生成
type SalesReportGenerator struct {
ReportGenerator
}
func (r *SalesReportGenerator) FetchData() {
fmt.Println("获取销售数据")
}
func (r *SalesReportGenerator) ProcessData() {
fmt.Println("处理销售数据")
}
func (r *SalesReportGenerator) OutputReport() {
fmt.Println("输出销售报表")
}
// 具体类 - 财务报表生成
type FinancialReportGenerator struct {
ReportGenerator
}
func (r *FinancialReportGenerator) FetchData() {
fmt.Println("获取财务数据")
}
func (r *FinancialReportGenerator) ProcessData() {
fmt.Println("处理财务数据")
}
func (r *FinancialReportGenerator) OutputReport() {
fmt.Println("输出财务报表")
}
func main() {
// 创建销售报表生成器
salesReport := &SalesReportGenerator{}
salesReport.GenerateReport()
// 创建财务报表生成器
financialReport := &FinancialReportGenerator{}
financialReport.GenerateReport()
}
适用场景
- 当多个子类有相同的算法骨架,而只有部分步骤需要不同实现时。
- 当要在基类中定义算法的结构,同时允许子类提供具体实现时。
- 当实现代码重复且多次出现相同的算法结构时,模板方法模式可以减少代码重复。
实现方式
- 定义一个抽象类,包含模板方法和一些可以被子类重写的钩子方法。
- 实现具体的子类,重写抽象类中的钩子方法,提供特定的实现。
- 客户端代码使用抽象类定义的模板方法来执行算法,子类提供具体的实现细节。
优缺点
优点:
- 通过将算法的骨架定义在基类中,可以避免代码重复,并使得算法结构清晰。
- 子类可以选择性地重写某些步骤,从而实现不同的变体。
- 易于扩展,添加新步骤或修改现有步骤不会影响已存在的子类。
缺点:
- 可能会导致基类和子类之间的紧耦合,因为子类依赖于基类的模板方法。
- 如果模板方法中包含复杂的逻辑,可能会导致基类过于庞大和复杂。
其他模式的关系
- 模板方法模式与策略模式(Strategy Pattern):策略模式用于定义一系列算法并使其可以互换,而模板方法模式用于在基类中定义算法的骨架,并将某些步骤推迟到子类中实现。可以结合使用,例如在模板方法中使用策略模式来处理具体的步骤。
- 模板方法模式与工厂方法模式(Factory Method Pattern):工厂方法模式用于创建对象,而模板方法模式用于定义算法的骨架。可以结合使用,例如在模板方法中使用工厂方法模式来创建不同的对象。
- 模板方法模式与状态模式(State Pattern):状态模式用于根据对象的状态改变其行为,而模板方法模式用于在基类中定义算法的骨架。可以结合使用,例如在模板方法中使用状态模式来管理算法的步骤。