Strategy接口应窄而稳定,只接收输入、返回统一结果;避免生命周期方法,配置通过构造函数传入;策略实例须无状态或线程安全;简单场景可用函数类型替代;注册查找宜用编译期变量而非字符串映射。

Strategy 接口定义要避免暴露具体实现细节
Go 没有抽象类,靠接口实现策略抽象,但容易把 Do() 设计成带太多参数或返回具体结构体,导致调用方被迫了解每种策略的内部契约。应该让接口方法签名尽可能窄、稳定。
- 推荐只接收输入数据(如
input interface{}或明确 DTO 结构),返回统一结果类型(如error或Result) - 避免在接口里定义
Init()、Close()等生命周期方法——那是策略工厂或上下文的责任 - 如果策略需要配置,通过构造函数传入,而非接口方法参数;例如
NewRateLimitStrategy(<code>maxRequests int)
运行时切换策略必须控制好状态共享边界
常见错误是多个 goroutine 并发调用同一个策略实例,而该策略内部用了非线程安全的字段(比如 map 未加锁、计数器未用 atomic)。切换本身不危险,危险的是「旧策略还在跑,新策略已生效」导致状态混乱。
- 策略实例应设计为无状态(preferred),或至少是只读状态(如预加载的规则表)
- 若必须维护状态(如滑动窗口限流器),确保每个策略实例独占一份,不要复用
- 切换动作建议放在初始化阶段或配置热重载入口,而不是在高频请求路径中频繁调用
SetStrategy()
用函数类型替代接口能简化简单场景
当策略逻辑极轻量(比如只是不同排序规则、简单校验函数),硬套接口反而增加冗余。Go 的函数是一等公民,直接用 type StrategyFunc func(input string) bool 更清晰。
- 可直接赋值匿名函数:
strategy := StrategyFunc(func(s string) bool { return len(s) > 5 }) - 便于测试:不用 mock 接口,直接传测试函数
- 注意:无法附加方法或字段,所以不适合需要配置、依赖注入或组合的复杂策略
别忽略策略注册与查找的性能开销
有些实现用 map[string]Strategy 做运行时注册,再通过字符串名查找——这在高并发下可能成为瓶颈,尤其当策略数量多或查找频繁时。
立即学习“go语言免费学习笔记(深入)”;
- 优先用编译期确定的变量引用,而非字符串查找;例如
strategies["rate_limit"]改为RateLimitStrategy全局变量 - 如果必须动态选型,缓存查找结果(如 sync.Map 或预先构建 switch 分支)
- 警惕反射方案:用
interface{}+reflect.Value.Call实现泛型策略调度,调试难、性能差、IDE 不友好










