在日常开发中,我们经常会遇到需要根据不同类型执行不同逻辑的场景。最直观的做法是用switch case
来处理,但随着业务扩展,case
分支会越来越多,代码变得臃肿难维护。今天我们以电商支付系统为例,聊聊如何用 Go 的策略模式替代switch case
,实现逻辑解耦和灵活扩展。
一、从一个真实痛点说起:支付方式的 "case 膨胀"
假设我们要开发一个电商订单支付模块,支持微信支付、支付宝、银行卡三种方式。每种支付方式的流程如下:
- 发起支付(调用不同平台接口)
- 处理回调(解析不同格式的回调参数)
- 计算优惠(不同支付方式有不同的优惠规则)
如果用传统switch case
实现,代码大概是这样的:
go
// 传统实现:用switch case处理不同支付方式
func processPayment(payType string, orderID string, amount float64) (string, error) {
switch payType {
case "wechat":
// 微信支付逻辑:生成支付链接
// 微信优惠计算:新用户满100减10
// ...(可能几百行代码)
case "alipay":
// 支付宝逻辑:生成支付表单
// 支付宝优惠:花呗满200减15
// ...(可能几百行代码)
case "bank":
// 银行卡逻辑:调用银行网关
// 银行卡优惠:信用卡免手续费
// ...(可能几百行代码)
default:
return "", fmt.Errorf("未知支付方式")
}
}
随着业务发展,问题会逐渐暴露:
- 新增支付方式(如 PayPal、ApplePay)需要修改这个函数,违反 "开闭原则"
- 所有逻辑堆在一个函数里,代码冗长(实际场景可能上千行)
- 不同支付方式的逻辑无法独立测试和复用
- 某个支付方式的修改可能影响其他支付逻辑,风险高
二、策略模式:让每种支付方式 "各管一摊"
策略模式的核心思想是:将一系列算法(这里是不同支付方式的逻辑)封装成独立的策略,让它们可以相互替换,且不影响使用算法的客户端。
在 Go 中,我们通过 "接口 + 结构体" 实现策略模式,步骤如下:
步骤 1:定义策略接口(统一行为规范)
首先定义一个PaymentStrategy
接口,声明所有支付方式都必须实现的核心方法:
go
运行
go
// PaymentStrategy 支付策略接口
type PaymentStrategy interface {
// InitPayment 发起支付,返回支付链接/参数
InitPayment(orderID string, amount float64) (string, error)
// HandleCallback 处理支付回调,返回订单状态
HandleCallback(callbackData map[string]string) (string, error)
// CalculateDiscount 计算该支付方式的优惠金额
CalculateDiscount(amount float64) float64
}
接口就像一份 "协议",规定了所有支付方式必须具备的能力,保证了不同策略的 "可替换性"。
步骤 2:实现具体策略(每种支付方式单独封装)
为每种支付方式定义结构体,并实现PaymentStrategy
接口,将各自的逻辑封装起来。
微信支付策略:
go
// WechatPayment 微信支付策略
type WechatPayment struct {
AppID string // 微信开放平台AppID
MchID string // 商户号
APIKey string // 支付密钥
IsNewUser bool // 是否新用户(影响优惠)
}
// InitPayment 发起微信支付
func (w *WechatPayment) InitPayment(orderID string, amount float64) (string, error) {
return fmt.Sprintf("https://wechat.pay.com?orderId=%s&amount=%.2f", orderID, amount), nil
}
// HandleCallback 处理微信回调
func (w *WechatPayment) HandleCallback(data map[string]string) (string, error) {
if data["return_code"] != "SUCCESS" {
return "", fmt.Errorf("微信支付失败:%s", data["err_code_des"])
}
return fmt.Sprintf("订单%s微信支付成功", data["out_trade_no"]), nil
}
// CalculateDiscount 微信支付优惠(新用户满100减10)
func (w *WechatPayment) CalculateDiscount(amount float64) float64 {
if w.IsNewUser && amount >= 100 {
return 10.0
}
return 0
}
支付宝策略:
go
// Alipay 支付宝策略
type Alipay struct {
AppID string // 支付宝AppID
PrivateKey string // 商户私钥
UseHuabei bool // 是否支持花呗
}
// InitPayment 发起支付宝支付
func (a *Alipay) InitPayment(orderID string, amount float64) (string, error) {
return fmt.Sprintf("https://alipay.com?outTradeNo=%s&amount=%.2f", orderID, amount), nil
}
// HandleCallback 处理支付宝回调
func (a *Alipay) HandleCallback(data map[string]string) (string, error) {
if data["trade_status"] != "TRADE_SUCCESS" {
return "", fmt.Errorf("支付宝支付失败:%s", data["sub_msg"])
}
return fmt.Sprintf("订单%s支付宝支付成功", data["out_trade_no"]), nil
}
// CalculateDiscount 支付宝优惠(花呗满200减15)
func (a *Alipay) CalculateDiscount(amount float64) float64 {
if a.UseHuabei && amount >= 200 {
return 15.0
}
return 0
}
(银行卡支付策略类似,此处省略)
每个策略结构体只关心自己的逻辑,代码量可控,且可以独立测试。
步骤 3:定义策略容器(统一调用入口)
创建一个 "策略容器" 结构体,持有PaymentStrategy
接口,对外提供统一的支付处理方法:
go
// OrderPaymentProcessor 订单支付处理器(策略容器)
type OrderPaymentProcessor struct {
strategy PaymentStrategy // 持有支付策略接口
}
// ProcessPayment 处理支付流程(统一入口)
func (p *OrderPaymentProcessor) ProcessPayment(orderID string, amount float64) (string, float64, error) {
discount := p.strategy.CalculateDiscount(amount) // 计算优惠
payURL, err := p.strategy.InitPayment(orderID, amount-discount) // 发起支付
return payURL, discount, err
}
// ProcessCallback 处理支付回调(统一入口)
func (p *OrderPaymentProcessor) ProcessCallback(data map[string]string) (string, error) {
return p.strategy.HandleCallback(data)
}
容器的作用是 "隔离变化":外部调用方无需关心具体支付方式,只需通过容器的方法即可完成支付流程。
三、使用策略模式:简洁且灵活
使用时,只需根据用户选择的支付方式,初始化对应的策略并注入容器:
go
func main() {
// 1. 微信支付(新用户)
wechatProcessor := &OrderPaymentProcessor{
strategy: &WechatPayment{
AppID: "wx123456",
MchID: "mch789",
IsNewUser: true,
},
}
payURL, discount, _ := wechatProcessor.ProcessPayment("ORDER_001", 150.0)
fmt.Printf("微信支付:链接=%s,优惠=%.2f\n", payURL, discount) // 优惠10元
// 2. 支付宝(花呗支付)
alipayProcessor := &OrderPaymentProcessor{
strategy: &Alipay{
AppID: "alipay_789",
UseHuabei: true,
},
}
payURL, discount, _ = alipayProcessor.ProcessPayment("ORDER_002", 200.0)
fmt.Printf("支付宝:链接=%s,优惠=%.2f\n", payURL, discount) // 优惠15元
// 处理回调
wechatCallback := map[string]string{"return_code": "SUCCESS", "out_trade_no": "ORDER_001"}
result, _ := wechatProcessor.ProcessCallback(wechatCallback)
fmt.Println("回调结果:", result) // 订单ORDER_001微信支付成功
}
四、策略模式的优势:为什么比 switch case 好?
- 消除冗长的条件判断 :用接口多态替代
switch case
,代码结构更清晰,可读性提升。 - 符合开闭原则 :新增支付方式(如 PayPal)时,只需新增一个实现
PaymentStrategy
的结构体,无需修改现有代码。 - 职责单一,易于维护:每种支付方式的逻辑封装在独立结构体中,修改某一种支付方式不会影响其他逻辑。
- 便于测试:可以单独对每种支付策略进行单元测试,无需搭建复杂的测试环境。
- 动态切换策略:运行时可以根据需求切换支付策略(如用户切换支付方式),灵活性更高。
五、什么时候适合用策略模式?
- 有多种相似的算法 / 逻辑(如不同支付方式、不同排序算法)
- 这些算法 / 逻辑需要频繁切换或扩展
- 代码中出现大量
switch case
或if-else
,且分支持续增长
如果只是简单的三四个分支且未来不会扩展,switch case
可能更简洁。但对于业务复杂且多变的场景,策略模式带来的可维护性提升是巨大的。
总结
Go 虽然没有类和继承,但通过 "接口 + 结构体" 的组合,能非常优雅地实现策略模式。在电商支付这类场景中,用策略模式替代switch case
,不仅能解决 "case 膨胀" 的问题,还能让代码更符合 "高内聚、低耦合" 的设计原则。