一、定义
1.1 核心概念
模板方法模式 (Template Method Pattern) 是一种行为设计模式,它在一个方法中定义了一个算法的骨架,并将某些步骤推迟到子类中实现。模板方法允许子类在不改变算法结构的情况下,重新定义算法中的某些步骤。
1.2 官方定义
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
1.3 Go语言特性
在Go语言中,由于没有类和继承的概念,模板方法模式通常通过以下方式实现:
- 接口:定义算法步骤
- 结构体:作为模板和具体实现
- 组合:代替继承
- 函数类型:作为回调
二、模式结构
2.1 核心组件
组件1:模板接口(抽象接口)
TemplateInterface抽象接口 - 定义算法的骨架方法
Step1Step2原语操作(Primitive Operations)- 必须由子类实现
Step1-- 步骤1:抽象方法,必须实现Step2-- 步骤2:抽象方法,必须实现
HookStepHookAfterStep钩子方法(Hook Methods)- 可选实现,控制流程
HookStep-- 钩子方法:决定是否执行某个步骤HookAfterStep-- 钩子方法:步骤后处理(可空实现)
go
type TemplateInterface interface {
Step1()
Step2()
HookStep() bool
HookAfterStep()
// 具体方法(Concrete Methods)- 可在接口中提供默认实现
// 在Go中,通常通过嵌入结构体或提供辅助函数来实现
}
我的疑问:
1、HookAfterStep-- 钩子方法:步骤后处理(可空实现)的 可空实现 是什么意思?
钩子方法有两种类型 :一种是 返回布尔值 ,用于控制算法流程(如是否执行某一步);另一种是 空方法 ,子类可以选择性地覆盖,以在算法的特定点插入自定义行为。HookAfterStep() 就是第二种钩子方法,它在模板方法的某个步骤之后被调用,默认是空实现,子类可以根据需要覆盖它,执行一些额外的操作
2、为什么这些方法都没有方法体?
Go 语言特性之一:Go 接口仅声明方法签名 ,不能包含方法体
组件2:模板结构体
情况一: 无状态模板(空结构体模板)
空结构体,零内存占用
go
type Template struct{}
情况二: 有状态模板(带配置和上下文)
有状态模板包含的三类信息:
- 配置信息 (Configuration) - 影响行为
- 执行上下文 (Context) - 记录当前执行
- 基础设施 (Infrastructure) - 辅助功能
go
type Template struct {
// 配置信息 (Configuration) - 影响行为
config *TemplateConfig // 如何处理任务
// 执行上下文 (Context) - 记录当前执行
context *TemplateContext // 执行过程中的状态
// 基础设施 (Infrastructure) - 辅助功能
logger Logger // 记录日志
metrics Metrics // 收集指标
cache Cache // 缓存数据
db Database // 数据库连接
}
配置结构体 - 定义模板的行为方式
go
type TemplateConfig struct {
// 1. 开关配置
EnableLogging bool // 是否启用日志
EnableMetrics bool // 是否收集指标
EnableCaching bool // 是否启用缓存
StrictMode bool // 是否严格模式(不允许跳过步骤)
// 2. 限制配置
MaxRetries int // 最大重试次数
Timeout time.Duration // 超时时间
MaxConcurrency int // 最大并发数
MaxItems int // 最大处理项数
// 3. 算法配置
Strategy string // 处理策略("fast", "safe", "balanced")
Priority int // 优先级
QualityLevel string // 质量级别("low", "medium", "high")
// 4. 资源配置
MemoryLimit int64 // 内存限制(字节)
TempDir string // 临时目录
OutputFormat string // 输出格式
}
上下文结构体 - 记录单个执行过程的状态
go
type TemplateContext struct {
// 1. 时间信息
StartTime time.Time // 开始时间
EndTime time.Time // 结束时间(完成后设置)
LastUpdateTime time.Time // 最后更新时间
// 2. 进度信息
StepCount int // 当前步骤数
TotalSteps int // 总步骤数
CurrentStep string // 当前步骤名
Progress float64 // 进度百分比(0.0-1.0)
// 3. 资源信息
MemoryUsed int64 // 已使用内存
CPUUsage float64 // CPU使用率
DiskUsage int64 // 磁盘使用量
// 4. 结果信息
TotalCost float64 // 总花费(金钱、时间等)
StepResults []StepResult // 每一步的结果
FinalResult interface{} // 最终结果
// 5. 状态信息
Status string // 状态("pending", "running", "completed", "failed")
Error error // 错误信息(如果有)
RetryCount int // 重试次数
Cancelled bool // 是否被取消
// 6. 业务数据
InputData interface{} // 输入数据
OutputData interface{} // 输出数据
Metadata map[string]interface{} // 元数据
}
基础设施接口定义
go
type Logger interface {
Info(msg string, args ...interface{})
Error(msg string, args ...interface{})
Debug(msg string, args ...interface{})
WithFields(fields map[string]interface{}) Logger
}
type Metrics interface {
IncrementCounter(name string, tags map[string]string)
RecordHistogram(name string, value float64, tags map[string]string)
SetGauge(name string, value float64, tags map[string]string)
}
type Cache interface {
Get(key string) (interface{}, bool)
Set(key string, value interface{}, ttl time.Duration)
Delete(key string)
}
type Database interface {
// 查询方法
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
Exec(query string, args ...interface{}) (sql.Result, error)
// 事务方法
Begin() (*sql.Tx, error)
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
// 连接管理
Ping() error
Close() error
// 统计信息
Stats() sql.DBStats
}
组件3:模板方法
模板方法 (Template Method) : 这是一个定义在模板中的方法,它提供了一个算法的骨架,按照一定的顺序调用其他方法(包括抽象方法和具体方法)。模板方法通常是不可被重写 的,它们代表了算法中不可变的步骤 。(在Go中,我们可以通过不暴露该方法为接口的一部分来防止重写,但这里我们使用结构体嵌入,子类可以重写,但通常我们不希望子类重写模板方法,因为这会改变算法结构)。
抽象方法 (Abstract Methods) : 这些方法在模板中声明(通常是通过接口),但是由子类实现。它们代表了算法中可变的步骤 。
具体方法 (Concrete Methods) : 这些方法在模板中已经有默认实现,子类可以选择使用或重写。
钩子方法 (Hook Methods) : 钩子方法是在模板中声明并提供了默认实现的方法,子类可以选择是否重写。它们通常用于在算法的特定点提供扩展能力。
go
// 模板方法 (Template Method) - 定义在模板中
func (t *Template) Execute(impl TemplateInterface) error {
// 算法的骨架 - 固定顺序
t.beforeExecute() // 具体方法
impl.Step1() // 抽象方法
if impl.HookStep() { // 钩子方法控制流程
impl.Step2() // 抽象方法
}
t.afterExecute() // 具体方法
impl.HookAfterStep() // 钩子方法
return nil
}
// 具体方法 (Concrete Methods) - 在模板中提供默认实现
func (t *Template) beforeExecute() {
fmt.Println("默认前置处理")
}
func (t *Template) afterExecute() {
fmt.Println("默认后置处理")
}
| 概念 | Go语言实现方式 | 作用 | 是否必须 | 能否被覆盖 |
|---|---|---|---|---|
| 模板方法 | 模板结构体的方法 | 定义算法的骨架和固定执行顺序 | 是 | 不应被覆盖 |
| 抽象方法 | 接口中的方法 | 声明必须由子类实现的步骤 | 接口要求必须实现 | 必须实现 |
| 具体方法 | 模板结构体的方法 | 提供默认实现,处理通用逻辑 | 否(有默认) | 可选择性覆盖 |
| 钩子方法 | 接口中的方法 | 提供扩展点,控制流程分支 | 是(但可为空) | 可选择性覆盖 |
组件4:具体实现类
具体实现类是模板方法模式中实现接口具体逻辑的类,它:
- 实现接口:必须实现所有抽象方法
- 继承/嵌入模板:获得模板的默认实现
- 提供具体业务逻辑:实现特定的功能
go
// 具体实现类
type ConcreteImpl struct {
Template // 嵌入模板,获得具体方法的默认实现
}
// 实现抽象方法 (必须)
func (c *ConcreteImpl) Step1() {
fmt.Println"实现 Step1")
}
func (c *ConcreteImpl) Step2() {
fmt.Println"实现 Step2")
}
// 实现钩子方法 (可选,但有默认行为)
func (c *ConcreteImpl) HookStep() bool {
return true // 默认返回 true
}
func (c *ConcreteImpl)Hook AfterStep() {
// 空实现 - 什么也不做
}
// 覆盖具体方法 (可选)
func (c *ConcreteImpl) beforeExecute() {
// 调用父类的默认实现
c.Template.beforeExecute()
fmt.Println("扩展前置处理")
}
func (c *ConcreteImpl) afterExecute() {
// 调用父类的默认实现
c.Template.afterExecute()
fmt.Println("扩展后置处理")
}
实例化
go
func main() {
// 创建模板实例
template := &Template{}
// 测试具体实现
impl := ConcreteImpl{}
template.Execute(impl)
}
bash
扩展前置处理
实现 Step1
实现 Step2
扩展后置处理
2.2 Mermaid 图表
UML类图详解
时序图
类关系与调用流程图
附件
角色养成
尝试
go
package main
import "fmt"
// 角色养成:体力 --> 收集材料 -- 等级升级 --> 体力 --> 刷材料 --> 武器升级 --> 体力 --> 刷材料 --> 天赋升级 --> 体力 --> 刷圣遗物 --> 装备强化
// 树脂 --> 获取材料 --> 升级
// CharacterDevelopment 角色养成
type CharacterDevelopment interface {
Resin() // 树脂
Material() // 刷材料
Upgrade() // 升级
Equip() // 装备
IfRetain() bool // 是否保留(圣遗物升级)
}
// SacredRelic 圣遗物
type SacredRelic struct {
}
func (s *SacredRelic) Execute(c CharacterDevelopment) error {
s.beforeExecute()
c.Resin()
c.Material()
c.Upgrade()
if c.IfRetain() == true {
c.Equip()
}
s.afterExecute()
return nil
}
func (s *SacredRelic) beforeExecute() {
fmt.Println("圣遗物养成开始")
}
func (s *SacredRelic) afterExecute() {
fmt.Println("圣遗物养成结束")
}
type Venti struct {
}
func (v Venti) Resin() {
fmt.Println("等待树脂恢复,目前有180树脂")
}
func (v Venti) Material() {
fmt.Println("刷圣遗物:翠绿之影4件套(风套)")
}
func (v Venti) Upgrade() {
fmt.Println("有个精通头,副词条有双爆,升级它")
}
func (v Venti) IfRetain() bool {
return true
}
func (v Venti) Equip() {
fmt.Println("副词条全中有双爆,保留它,现在装备上")
}
func main() {
sacredRelic := &SacredRelic{}
venti := Venti{}
err := sacredRelic.Execute(venti)
if err != nil {
return
}
}
bash
圣遗物养成开始
等待树脂恢复,目前有180树脂
刷圣遗物:翠绿之影4件套(风套)
有个精通头,副词条有双爆,升级它
副词条全中有双爆,保留它,现在装备上
圣遗物养成结束