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

用函数类型定义策略接口
Go 没有传统面向对象的 interface 实现继承,但可以用函数类型直接建模“可替换的行为”。策略的核心是统一调用签名,比如 func(int, int) int 就能代表所有二元整数运算策略。
定义策略类型时,别写空接口或结构体包装器——直接用函数类型更轻量、更符合 Go 习惯:
type Calculator func(int, int) intvar Add Calculator = func(a, b int) int { return a + b } var Mul Calculator = func(a, b int) int { return a * b }
这样定义后,Add 和 Mul 就是具体策略,可直接传参、赋值、闭包捕获上下文。
把策略作为参数传给上下文函数
策略模式的关键是“上下文不关心策略怎么实现,只负责调用”。在 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 或打开了文件,必须由调用方保证清理,这点比结构体策略更隐晦。










