go中策略模式核心是运行时安全切换、依赖可注入、新增策略不改旧代码;接口仅保留纯行为方法,依赖通过构造函数注入,构造函数返回接口类型并校验必要参数,用rwmutex保护运行时切换,禁用map函数式伪策略。

Go 语言里实现策略模式,核心不是“怎么写接口”,而是“怎么避免把策略写成一堆 if-else 或全局变量”。真正能落地的策略模式,必须满足三个条件:运行时可安全切换、依赖可注入、新增策略不改旧代码。
策略接口只留一个方法,别塞日志器或配置字段
很多人一上来就定义这样的接口:
type PaymentStrategy interface {
Logger() *log.Logger
Config() map[string]string
Pay(amount float64) error
}
结果每个实现都得写重复的 Logger() 方法,还容易漏校验——这不是策略,是模板方法反模式。
- 策略接口只保留「纯行为契约」,比如
Pay(amount float64) error或Sort(data []int) []int - 共享依赖(如
*log.Logger、*http.Client)全部通过构造函数传入,不在接口里暴露 - 接口名用名词或形容词,比如
SortingStrategy,别加I前缀或Interface后缀
构造函数必须返回接口,不能返回具体结构体指针
错误写法:func NewAlipay(...) *Alipay;正确写法:func NewAlipay(...) PaymentStrategy。前者会让调用方强依赖具体类型,换策略时得改所有 new(Alipay) 的地方。
- 所有
NewXXX()函数统一返回策略接口类型,上层只认接口,不认实现 - 构造函数里做必要非空校验,比如
if client == nil { panic("client is required") },但更推荐返回(PaymentStrategy, error) - 别在构造函数里做耗时操作(如初始化 HTTP 连接池),策略实例应轻量、即取即用
运行时切换必须带读写锁,别碰全局变量
直接声明 var currentStrategy PaymentStrategy + SetStrategy(),并发下必 panic:多个 goroutine 同时写这个变量,没同步机制。
- 把策略封装进服务结构体,用
sync.RWMutex保护字段读写,读多写少场景下性能比sync.Mutex更好 - HTTP handler 里禁止直接调用
SetStrategy()——一个请求改了,所有后续请求策略全变 - 如果策略只在启动时定死(比如从配置文件加载一次),用
sync.Once初始化即可,不用锁
别用 map[string]func 替代策略,它根本不是策略模式
看到有人用 map[string]func(float64) error 实现“支付路由”,这看起来短,但很快失控:
- 新增策略要同时改注册表和函数体,违反开闭原则
- 无法携带状态(比如微信支付需要
appID和client,纯函数做不到) - 没法单元测试:你 mock 不了一个匿名函数,但可以轻松 mock 接口
- 错误处理、重试、日志全得在每个函数里重复写,没法复用
真正复杂的业务里,策略不是“选一个函数”,而是“选一个带依赖、有生命周期、可监控、可灰度的组件”。这点最容易被忽略——把策略当函数用,迟早要返工。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











