用 Go 策略模式替代 Switch Case:电商支付场景的解耦实践

在日常开发中,我们经常会遇到需要根据不同类型执行不同逻辑的场景。最直观的做法是用switch case来处理,但随着业务扩展,case分支会越来越多,代码变得臃肿难维护。今天我们以电商支付系统为例,聊聊如何用 Go 的策略模式替代switch case,实现逻辑解耦和灵活扩展。

一、从一个真实痛点说起:支付方式的 "case 膨胀"

假设我们要开发一个电商订单支付模块,支持微信支付、支付宝、银行卡三种方式。每种支付方式的流程如下:

  1. 发起支付(调用不同平台接口)
  2. 处理回调(解析不同格式的回调参数)
  3. 计算优惠(不同支付方式有不同的优惠规则)

如果用传统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 好?

  1. 消除冗长的条件判断 :用接口多态替代switch case,代码结构更清晰,可读性提升。
  2. 符合开闭原则 :新增支付方式(如 PayPal)时,只需新增一个实现PaymentStrategy的结构体,无需修改现有代码。
  3. 职责单一,易于维护:每种支付方式的逻辑封装在独立结构体中,修改某一种支付方式不会影响其他逻辑。
  4. 便于测试:可以单独对每种支付策略进行单元测试,无需搭建复杂的测试环境。
  5. 动态切换策略:运行时可以根据需求切换支付策略(如用户切换支付方式),灵活性更高。

五、什么时候适合用策略模式?

  • 有多种相似的算法 / 逻辑(如不同支付方式、不同排序算法)
  • 这些算法 / 逻辑需要频繁切换或扩展
  • 代码中出现大量switch caseif-else,且分支持续增长

如果只是简单的三四个分支且未来不会扩展,switch case可能更简洁。但对于业务复杂且多变的场景,策略模式带来的可维护性提升是巨大的。

总结

Go 虽然没有类和继承,但通过 "接口 + 结构体" 的组合,能非常优雅地实现策略模式。在电商支付这类场景中,用策略模式替代switch case,不仅能解决 "case 膨胀" 的问题,还能让代码更符合 "高内聚、低耦合" 的设计原则。

相关推荐
Penge6664 小时前
《重构:改善既有代码的设计》
后端
BingoGo4 小时前
PHP8.5 的新 URI 扩展
后端·php
golang学习记4 小时前
Go 1.26 新特性前瞻:new 函数支持表达式参数,性能提升30%
后端
召摇4 小时前
在浏览器中无缝运行Go工具:WebAssembly实战指南
后端·面试·go
召摇4 小时前
Spring Security入门指南
后端·spring·面试
笃行3504 小时前
Ubuntu 22.04 服务器安装 KingbaseES 电科金仓数据库详细教程
后端
数据小馒头4 小时前
浅谈SQL审核(一):SQL审核实现方式与常见工具的选择
后端
武子康5 小时前
大数据-128 - Flink 并行度详解:从概念到最佳实践,一文读懂任务并行执行机制 代码示例与性能优化
大数据·后端·flink