应使用 interface{} 定义策略当算法差异大、生命周期独立且不共享状态时,如支付方式;避免将共用字段强塞入接口,宜用组合或工厂;策略应无条件判断,条件选择前置;函数类型无法携带状态和依赖,不利测试与维护;DI 与插件策略可分层处理。
interface{} 定义策略而不是具体类型当多个算法逻辑差异大、生命周期独立、且不共享内部状态时,用 interface{} 定义统一入口最稳妥。比如支付方式:PayMethod 接口只暴露 Process(amount float64) error,而 Alipay、WechatPay、CreditCard 各自实现,互不耦合。
反例是强行把带大量共用字段的结构体塞进策略接口——这时应该考虑组合或工厂封装,而非硬套策略模式。
ctx context.Context)
X-Payment-Method 选实现)switch 分支太多?可能是策略没抽对粒度常见错误:把「按用户等级打折」、「按商品类目打折」、「按促销活动打折」全塞进一个 DiscountStrategy 接口,结果每个实现里又写一堆 if 或 switch 判断条件——这说明策略边界模糊,不是策略模式用错了,是策略拆错了。
正确做法是让策略本身无条件判断,条件判断提前到选择策略的环节。例如:
func NewDiscountStrategy(userLevel string, category string, promoID string) DiscountStrategy {
switch {
case promoID != "" && isValidPromo(promoID):
return &PromoDiscount{ID: promoID}
case userLevel == "vip":
return &VIPDiscount{}
case category == "electronics":
return &ElectronicsDiscount{}
default:
return &DefaultDiscount{}
}
}
这样每个策略实现干净,职责单一,测试也容易覆盖。
func(float64) error 替代接口函数类型轻量,适合极简场景(如日志格式化、简单校验),但策略模式真正价值在于「可携带状态 + 可依赖注入 + 可单元测试隔离」。用函数类型会丢失这些能力。
StripeClient 实例)例如,一个需要调用外部 API 的风控策略,必须是结构体+方法,而不是裸函数:
type RiskCheckStrategy struct {
client *http.Client
timeout time.Duration
}
func (r *RiskCheckStrategy) Check(orderID string) (bool, error) {
// 使用 r.client 发起请求
}
项目用了 Wire 或 fx 做依赖注入,又想支持插件式策略注册(比如从目录自动加载 .so 策略),这两者天然矛盾:DI 要求编译期可知,插件要运行期加载。
折中方案是分层:核心策略走 DI,扩展策略走工厂 + 显式注册。避免在 DI 图里直接注入未知类型。
RegisterStrategy(name string, s Strategy)
RegisterStrategy("redis", &RedisCache{}))GetStrategy("redis") 获取,不参与 DI 构建图关键点在于:策略实例的创建时机和生命周期必须明确分离,否则容易出现空指针或资源泄漏。