go策略模式应通过接口契约+构造函数注入实现,接口只定义可变行为(如pay方法),避免冗余方法和字段,构造函数校验依赖非空,运行时切换需用rwmutex保护,动态路由用注册表而非硬编码。

Go 语言策略模式不是靠继承或抽象类,而是靠接口契约 + 构造函数注入 + 字段赋值来实现的;它不难,但错一处就容易 panic 或并发不安全。
怎么定义策略接口才不会踩坑
接口不是用来塞公共字段或生命周期方法的,只保留真正会变的行为。比如支付策略,Pay(amount float64) error 就够了——加 Init()、Close()、Logger() 都是典型错误。
- 接口名用名词或形容词,比如
PaymentStrategy,别写IPaymentStrategy - 方法必须导出(首字母大写),否则其他包无法实现
- 别为“以后可能加”提前加参数,比如现在没上下文需求,就别硬塞
ctx context.Context,否则所有实现全得改 - 避免空接口
interface{}或map[string]interface{}存策略,类型断言失败是 runtime panic 的高发区
具体策略怎么初始化才安全
策略不是裸 struct,它大概率要依赖 client、配置、日志器等——这些必须通过构造函数传入,且在创建时校验非空。
- 构造函数统一用
NewXXX命名,返回接口类型(如PaymentStrategy),不是具体结构体指针 - 别在
Pay()方法里做 heavy 初始化(比如读文件、建 HTTP 连接),首次调用延迟不可控 - 如果初始化可能失败(如密钥格式错误),返回
(PaymentStrategy, error),别panic - 示例:
func NewAlipay(client *alipay.Client, logger *log.Logger) PaymentStrategy,内部检查client != nil
运行时切换策略为什么不能用全局变量
用 var currentStrategy PaymentStrategy + SetStrategy() 看似简单,但多个 goroutine 同时写就会触发 data race,HTTP handler 里这么干等于给服务埋雷。
- 正确做法是把策略作为结构体字段封装,用
sync.RWMutex保护读写 - 读多写少场景下,
RWMutex比Mutex性能更好 - 如果策略启动后就固定(如配置决定走微信还是支付宝),用
sync.Once初始化即可,无需锁 - 绝对不要在 handler 里直接改全局策略变量——一个请求改了,所有后续请求都跟着变
策略注册表怎么支持动态路由
硬编码 if order.Type == "vip" { useVIPStrategy() } 是反模式,新增策略就得改两处:注册逻辑 + if 分支,很快失控。
- 推荐轻量注册表:
map[string]PaymentStrategy或map[string]func() PaymentStrategy - 路由逻辑单独抽成函数,比如
func SelectStrategy(order Order) PaymentStrategy,按用户等级、地区、订单金额等条件返回对应策略 - 策略实例应无状态,或状态严格绑定到单次调用(比如把 retryCount 放在
Process()参数里,而不是策略 struct 字段中) - 单元测试时,手工 mock 实现接口比 mock 函数更可控,也更容易验证行为选择是否正确
最常被忽略的一点:策略对象不是线程安全的容器,如果它带可变字段(比如计数器、缓存 map),又多个请求共用同一个实例,结果就是状态污染——看起来像随机 bug,其实是共享状态没隔离。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











