Go -- 模板方法模式 (Template Method)

一、定义

1.1 核心概念

模板方法模式 (Template Method Pattern) 是一种行为设计模式,它在一个方法中定义了一个算法的骨架,并将某些步骤推迟到子类中实现。模板方法允许子类在不改变算法结构的情况下,重新定义算法中的某些步骤。

1.2 官方定义

定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

1.3 Go语言特性

在Go语言中,由于没有类和继承的概念,模板方法模式通常通过以下方式实现:

  • 接口:定义算法步骤
  • 结构体:作为模板和具体实现
  • 组合:代替继承
  • 函数类型:作为回调

二、模式结构

2.1 核心组件

组件1:模板接口(抽象接口)

TemplateInterface 抽象接口 - 定义算法的骨架方法

Step1 Step2 原语操作(Primitive Operations)- 必须由子类实现

  • Step1-- 步骤1:抽象方法,必须实现
  • Step2-- 步骤2:抽象方法,必须实现

HookStep HookAfterStep 钩子方法(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类图详解


classDiagram direction TB %% Go语言实现细节 class TemplateInterface { <> +Step1() +Step2() +HookStep() bool +HookSAfterStep() } class Template { <> #Execute(impl TemplateInterface) error #beforeExecute() #afterExecute() } class ConcreteImpl { <> -Template +Step1() +Step2() +HookStep() bool +HookSAfterStep() #beforeExecute() #afterExecute() } %% 方法可见性 note for Template "Go语言方法可见性:\n大写开头: 公开\n小写开头: 包内可见" %% 关系说明 TemplateInterface <|.. ConcreteImpl : 实现接口 ConcreteImpl --* Template : 嵌入结构体(组合) %% 方法调用关系 note for Template "Execute() 调用:\n1. beforeExecute() - 具体方法\n2. impl.Step1() - 抽象方法\n3. impl.HookStep() - 钩子方法\n4. impl.Step2() - 抽象方法\n5. afterExecute() - 具体方法\n6. impl.HookAfterStep() - 钩子方法"

时序图


sequenceDiagram %% 初始化阶段 box rgba(200, 220, 240, 0.5) 初始化 participant C as 客户端 participant T as Template participant I as TemplateInterface participant CI as ConcreteImpl end %% 模板方法执行阶段 box rgba(220, 240, 200, 0.5) 执行阶段 participant Exec as Execute方法 participant Before as beforeExecute participant Step1 as Step1抽象 participant Hook as HookStep钩子 participant Step2 as Step2抽象 participant After as afterExecute participant AfterHook as HookAfterStep钩子 end %% 1. 初始化 C->>T: new Template() T-->>C: Template实例 C->>CI: new ConcreteImpl() CI->>T: 嵌入Template CI-->>C: ConcreteImpl实例 %% 2. 执行模板方法 C->>T: Execute(CI) T->>Exec: 进入模板方法 %% 3. 算法骨架执行 Exec->>Before: 调用前置处理 Note over Before: 具体方法 alt 实现类覆盖了beforeExecute Before->>CI: 调用覆盖的方法 CI->>T: 调用父类方法 T-->>CI: 执行默认逻辑 CI-->>CI: 执行扩展逻辑 CI-->>Before: 完成 else 使用默认实现 Before->>T: 调用默认实现 T-->>Before: 完成 end Before-->>Exec: 前置处理完成 Exec->>Step1: 调用Step1() Step1->>CI: 调用具体实现 CI-->>Step1: 完成 Step1-->>Exec: Step1完成 Exec->>Hook: 调用HookStep() Hook->>CI: 调用具体实现 CI-->>Hook: 返回决策结果 Hook-->>Exec: 返回结果 alt 钩子返回true Exec->>Step2: 调用Step2() Step2->>CI: 调用具体实现 CI-->>Step2: 完成 Step2-->>Exec: Step2完成 else 钩子返回false Note over Exec: 跳过Step2 end Exec->>After: 调用后置处理 Note over After: 具体方法 alt 实现类覆盖了afterExecute After->>CI: 调用覆盖的方法 CI->>T: 调用父类方法 T-->>CI: 执行默认逻辑 CI-->>CI: 执行扩展逻辑 CI-->>After: 完成 else 使用默认实现 After->>T: 调用默认实现 T-->>After: 完成 end After-->>Exec: 后置处理完成 Exec->>AfterHook: 调用HookAfterStep() AfterHook->>CI: 调用具体实现 CI-->>AfterHook: 完成 AfterHook-->>Exec: 完成 Exec-->>T: 模板方法执行完成 T-->>C: 返回结果

类关系与调用流程图


flowchart TD subgraph A [客户端层] Client[客户端代码] end subgraph B [模板层] Template[Template结构体] TemplateMethods[具体方法] end subgraph C [接口层] Interface[TemplateInterface] end subgraph D [实现层] ConcreteImpl[ConcreteImpl结构体] ImplMethods[具体实现方法] end %% 创建关系 Client -->|创建| Template Client -->|创建| ConcreteImpl %% 嵌入关系 ConcreteImpl -->|嵌入| Template %% 实现关系 ConcreteImpl -->|实现| Interface %% 调用关系 Client -->|调用| Execute subgraph E [Execute方法内部调用] direction LR Execute[Execute模板方法] Step1[Step1抽象方法] Step2[Step2抽象方法] Hook[HookStep钩子方法] HookAfterHook[HookAfterStep钩子方法] Execute --> Before[beforeExecute] Before --> Step1 Step1 --> Hook Hook -->|true| Step2 Step2 --> After[afterExecute] Hook -->|false| After After -->HookAfterHook end %% 方法实现关系 Before -->|调用| TemplateMethods Step1 -->|调用| ImplMethods Hook -->|调用| ImplMethods Step2 -->|调用| ImplMethods After -->|调用| TemplateMethods HookAfterHook -->|调用| ImplMethods %% 样式 classDef client fill:#bbdefb,stroke:#1976d2 classDef template fill:#c8e6c9,stroke:#388e3c classDef interface fill:#f0f4c3,stroke:#afb42b classDef concrete fill:#ffccbc,stroke:#e64a19 classDef method fill:#e1bee7,stroke:#7b1fa2 class Client client class Template,TemplateMethods template class Interface interface class ConcreteImpl,ImplMethods concrete class Execute,Before,Step1,Step2,Hook,After,HookAfterHook method

附件

角色养成

尝试

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件套(风套)
有个精通头,副词条有双爆,升级它
副词条全中有双爆,保留它,现在装备上
圣遗物养成结束

优化

进阶

日志

相关推荐
喵个咪2 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:基于 Ent 从零实现新服务
后端·go·orm
踏浪无痕9 小时前
高并发写入 API 设计:借鉴 NSQ 的内存队列与背压机制
后端·面试·go
技术不打烊10 小时前
Go并发陷阱避坑:RWMutex与Channel最佳实践
网络协议·go
用户89535603282201 天前
Goroutine + Channel 高效在哪?一文吃透 Go 并发底层 G-M-P 调度与实现
后端·go
王中阳Go2 天前
RAG的教程还是Python的丰富呀,咱们也想办法给Go生态做做贡献吧,哈哈。
go
没逻辑2 天前
Gopher 带你学并发计数器:从最快到最慢的性能之旅
后端·go
rafael(一只小鱼)2 天前
gemini使用+部署教程
java·人工智能·ai·go
free-dancer2 天前
【go之一】GMP模型
go
Java水解3 天前
GO语言特性介绍,看这一篇就够了!
后端·go