Go中如何用函数实现策略模式_Go函数式策略模式示例

1次阅读

go中策略模式首选函数类型,如type Calculator func(int, int) int,直接定义Add、Mul等策略并作为参数传入上下文函数Compute,支持闭包捕获状态、动态选择与组合,轻量高效;需维护状态或多方法时才用结构体+Interface

Go中如何用函数实现策略模式_Go函数式策略模式示例

用函数类型定义策略接口

Go 没有传统面向对象的 interface 实现继承,但可以用函数类型直接建模“可替换的行为”。策略的核心是统一调用签名,比如 func(int, int) int 就能代表所有二元整数运算策略。

定义策略类型时,别写空接口或结构体包装器——直接用函数类型更轻量、更符合 Go 习惯:

type Calculator func(int, int) int 

var Add Calculator = func(a, b int) int { return a + b } var Mul Calculator = func(a, b int) int { return a * b }

这样定义后,AddMul 就是具体策略,可直接传参、赋值、闭包捕获上下文。

把策略作为参数传给上下文函数

策略模式的关键是“上下文不关心策略怎么实现,只负责调用”。在 Go 中,上下文通常就是一个普通函数,接收策略函数作为参数:

常见错误是把策略硬编码进结构体方法里,失去运行时切换能力;正确做法是让执行逻辑保持无状态,靠参数注入行为:

  • 避免写 type Processor Struct { op String } 再用 switch 分支——这不算策略模式,只是配置驱动
  • 应写 func Compute(a, b int, op Calculator) int { return op(a, b) },调用方决定用哪个策略
  • 支持闭包策略:比如 func makeThresholdAdder(threshold int) Calculator { return func(a, b int) int { ... } }

策略组合与运行时动态选择

函数式策略天然支持组合和条件选择,不需要工厂类或反射。例如根据输入动态选策略:

func GetStrategy(mode string) Calculator {     switch mode {     case "add": return Add     case "mul": return Mul     default:    return func(a, b int) int { return a - b }     } }

注意几个易错点:

  • 返回局部匿名函数时,若捕获了循环变量(如 for _, m := range modes { strategies[m] = func(){} }),所有策略会共享最后一个值——必须用显式参数传入:strategies[m] = func(m string) Calculator { return ... }(m)
  • 策略函数若需访问外部状态(如日志、配置),建议通过闭包捕获,而非依赖全局变量,否则测试难、并发不安全
  • 性能上,函数调用开销极小,比 interface 调用还略快;但若策略本身很重(如含 http 调用),注意不要在高频路径反复构造

和传统结构体策略对比:何时该用函数

不是所有场景都适合函数式策略。当策略需要维护内部状态(如计数器、连接池)、实现多个方法(Init/Close)、或需嵌入其他 interface 时,还是该用结构体+interface。

纯函数策略最适用的场景是:

  • 行为单一、无状态(如排序比较函数 func(a, b interface{}) bool
  • 策略数量少且固定(
  • 希望单元测试时能直接传入 mock 函数,而不是 mock 接口
  • 策略逻辑简单,写成函数比写结构体+方法更直观

真正容易被忽略的是生命周期管理——函数本身无析构,如果策略内启了 goroutine 或打开了文件,必须由调用方保证清理,这点比结构体策略更隐晦。

text=ZqhQzanResources