Go语言策略模式推荐三种实现:1. 简单无状态用函数类型;2. 带状态依赖用接口+结构体;3. 可扩展查找用map注册表,需注意错误统一、context透传与并发安全。

Go 语言没有类继承和接口实现的强制约束,策略模式不能照搬 Java/C# 那套写法;它更适合用函数类型、接口+结构体组合、或 map[string]func 的轻量方式来封装业务逻辑。
用 func 类型定义策略最简洁
当策略逻辑简单、无状态、不需复用内部字段时,直接用函数类型是最自然的选择。比如支付方式:微信支付、支付宝、银行卡,各自只需一个 Pay(amount float64) error 行为。
定义统一策略类型:
type PayStrategy func(amount float64) error
实现不同策略:
立即学习“go语言免费学习笔记(深入)”;
var WechatPay PayStrategy = func(amount float64) error {
fmt.Println("WeChat pay:", amount)
return nil
}
var Alipay PayStrategy = func(amount float64) error {
fmt.Println("Alipay:", amount)
return nil
}
使用时直接传入函数值,无需实例化结构体:
func DoPayment(strategy PayStrategy, amount float64) error {
return strategy(amount)
}
DoPayment(WechatPay, 99.9)
⚠️ 注意:这种写法无法在策略内部访问上下文(如用户 ID、订单号),也不便于加日志/监控中间件——需要升级为接口方式。
用接口 + 结构体实现带状态的策略
当策略需要持有配置、依赖外部服务(如调用某个 *http.Client 或数据库连接),就该用接口定义行为,结构体承载状态。
定义接口:
type PaymentProcessor interface {
Process(orderID string, amount float64) error
}实现微信支付策略:
type WechatProcessor struct {
client *http.Client
appID string
}
func (w *WechatProcessor) Process(orderID string, amount float64) error {
// 使用 w.client 和 w.appID 发起请求
fmt.Printf("WeChat processing %s for %.2f\n", orderID, amount)
return nil
}
工厂函数按需返回具体策略:
func NewPaymentProcessor(kind string, cfg Config) PaymentProcessor {
switch kind {
case "wechat":
return &WechatProcessor{client: &http.Client{}, appID: cfg.AppID}
case "alipay":
return &AlipayProcessor{gateway: cfg.AlipayURL}
default:
panic("unknown processor")
}
}✅ 这种方式支持依赖注入、单元测试 mock、运行时动态切换,是中大型业务推荐的写法。
用 map[string]PaymentProcessor 实现策略注册与查找
避免每次 if-else 或 switch 判断类型,把策略实例缓存起来,按 key 查找更清晰、可扩展性更强。
初始化全局策略注册表:
var processors = make(map[string]PaymentProcessor)func RegisterProcessor(name string, p PaymentProcessor) { processors[name] = p }
func GetProcessor(name string) (PaymentProcessor, bool) { p, ok := processors[name] return p, ok }
注册时:
RegisterProcessor("wechat", &WechatProcessor{...})
RegisterProcessor("alipay", &AlipayProcessor{...})
使用时:
if p, ok := GetProcessor("wechat"); ok {
p.Process("ORD-123", 199.0)
}⚠️ 注意:注册时机必须早于首次使用(通常放在 init() 或应用启动时);并发读写需加 sync.RWMutex(如果运行时会动态增删策略)。
策略切换时容易忽略的两个点
一是错误处理一致性:不同策略可能返回不同格式的错误(如网络超时 vs 参数校验失败),建议统一封装成自定义错误类型,带 Code() 方法便于上层分类响应。
二是上下文传递:别把 context.Context 硬编码进接口方法签名里——应该让所有策略方法都接收 ctx context.Context,否则超时控制、trace 透传都会断掉。
例如接口应定义为:
type PaymentProcessor interface {
Process(ctx context.Context, orderID string, amount float64) error
}不是所有策略都需要用到 ctx,但留着它,比后面补加更安全。










