欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

技术教程

Golang模板方法模式如何复用业务逻辑_代码结构说明

作者:P粉6029986702026-01-13 00:00:00
Go中模板方法模式通过组合+接口+函数参数实现,核心是将可变逻辑抽为func或接口方法由调用方注入;轻量场景用函数参数,重逻辑用窄接口,横切关注用结构体钩子封装。

Go 没有继承,所谓“模板方法模式”只能靠组合 + 接口 + 函数参数模拟,核心不是复用父类骨架,而是把可变逻辑抽成 func 或接口方法,由调用方注入。

用函数参数替代抽象方法

把算法中变化的部分定义为函数类型,主流程固定,变化点作为参数传入。这是最轻量、最符合 Go 习惯的做法。

  • templateMethod 不依赖任何结构体,只依赖输入参数和回调函数
  • 避免为单次定制逻辑定义新类型或实现接口
  • 适合逻辑差异小、生命周期短的业务场景(如不同渠道的订单校验)
type OrderValidator func(*Order) error

func ProcessOrder(order *Order, validate OrderValidator) error {
    if err := validate(order); err != nil {
        return err
    }
    if err := order.Charge(); err != nil {
        return err
    }
    return order.SendNotification()
}

用接口约束行为差异

当变化逻辑较重、需要复用状态或多次调用时,定义接口比裸函数更清晰。注意:接口应窄——只包含该模板真正需要的方法。

  • 接口名建议带 ProcessorStrategy 等后缀,避免泛称 Handler
  • 不要让接口暴露无关方法(比如 SaveLog),否则违反里氏替换
  • 实现类型内部可封装私有字段(如配置、客户端),但模板函数不感知
type OrderProcessor interface {
    Validate(*Order) error
    Charge(*Order) error
}

func RunOrderFlow(p OrderProcessor, order *Order) error {
    if err := p.Validate(order); err != nil {
        return err
    }
    return p.Charge(order)
}

组合结构体封装公共流程与钩子

当多个模板流程共享初始化、清理、日志、重试等横切逻辑时,用结构体组合比重复写函数更可控。关键在明确“钩子”的调用时机和默认行为。

  • 钩子字段(如 Before, After)类型统一为 func() error,便于零值安全调用
  • 主流程方法(如 Execute)不暴露内部状态,只接收必要参数
  • 避免在结构体里存业务数据;状态应由调用方传入,保持无副作用
type OrderTemplate struct {
    Before func() error
    After  func() error
}

func (t *OrderTemplate) Execute(order *Order, process func(*Order) error) error {
    if t.Before != nil {
        if err := t.Before(); err != nil {
            return err
        }
    }
    err := process(order)
    if t.After != nil {
        t.After() // 忽略 after 错误,避免掩盖主流程错误
    }
    return err
}

真正难的不是写模板,而是判断哪部分该抽象、哪部分该固化。业务规则常变,但数据流向和错误处理路径往往稳定;优先抽象的是“做什么”,而不是“怎么做”。