核心是用窄口径接口定义策略契约,客户端仅依赖接口,通过注入或运行时替换实现解耦;策略应无状态、轻量,构造函数返回接口,避免空接口与过度抽象,配合di框架按配置动态切换。

核心在于用接口定义契约,让具体算法只依赖这个契约,不暴露实现细节。客户端只和接口打交道,切换时只需换掉接口背后的实例,业务逻辑完全不动。
定义稳定、窄口径的策略接口
接口要小而专,只声明必须的方法,参数和返回值尽量简单。比如支付策略就只留 Pay(amount float64) error,别加 context.Context 或 logger —— 那些该由上下文或中间件注入,不是策略该管的。方法签名一旦定下,所有实现都得遵守,否则一换就崩。
- 避免空接口(interface{})或泛型过度抽象,容易在调用时 panic
- 接口名体现意图,如 DiscountStrategy、Sorter、Notifier
- 方法名动词开头,语义清晰,不带实现痕迹(比如不用 AlipayPay,就用 Notify)
上下文只持接口引用,不碰具体类型
上下文结构体里字段类型必须是策略接口,不是某个 struct 指针。它不关心是谁在干活,只负责把请求转发过去。切换策略就是给这个字段赋一个新接口值,没有 new、没有类型断言、不 import 具体策略包。
- 提供 SetStrategy(s StrategyInterface) 方法,支持运行时替换
- 构造函数可接受默认策略,但不强制绑定;允许后续覆盖
- 避免在上下文中缓存具体策略的字段或状态,那会破坏可替换性
策略实现无状态或状态隔离
每个策略实例最好轻量、无共享状态。如果有配置(如 API key、超时),应在创建时传入并固化,而不是每次调用都去读环境变量或全局 config。若必须维护状态(如重试计数),确保该状态属于当前请求生命周期,不跨 goroutine / 请求复用同一个实例。
- 推荐用工厂函数 NewXXX(config ...) 构造,返回接口,隐藏具体类型
- 构造失败返回 (Strategy, error),不 panic
- 禁止在策略方法内做耗时操作(如 HTTP 调用、DB 查询)—— 那是上下文或服务层的事
依赖注入配合运行时切换更安全
在 Spring、Laravel 或 Go 的 Wire/DI 框架中,把策略注册为可选服务,通过名称或标签区分。运行时根据配置(如 env、HTTP header、用户角色)从容器取对应策略实例,再塞进上下文。这样切换不靠硬编码 new,而是靠配置驱动,天然支持灰度、A/B 测试和热更新。
- 例如 Laravel 的 Manager 基类,driver('wechat') 自动解析并构造微信通知策略
- Spring 中用 @Qualifier("alipay") 注入指定支付策略
- Go 可用 map[string]StrategyInterface 缓存已初始化的策略,按 key 查找替换











