策略模式:支付方式选择——把算法封装成可替换的“零件”

大家好,我是你们的老朋友。今天我们来聊策略模式。

说实话,这个模式是我在实际项目中用得最多的几个模式之一。为什么?因为业务变化太快了。今天支持支付宝,明天要接微信,后天又来了个银联。你总不能每次都改核心代码吧?

策略模式就是来解决这个问题的。它的核心思想很简单:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。说白了,就是把变化的部分抽出来,变成可插拔的零件。

从生活场景理解策略模式

你想想看,电商平台的支付方式。用户下单后,可以选择支付宝、微信、银联。每种支付方式的接口、参数、签名算法都不一样。如果你在订单处理代码里写满 if-else,那代码会变成什么样?

我见过一个项目,支付逻辑里嵌套了七八层 if-else,每次新增支付方式都要改核心流程。改一次,测试回归一次,上线提心吊胆一次。嗯,这种滋味不好受。

策略模式的做法是:把每种支付方式封装成一个独立的策略类,它们实现同一个接口。订单处理代码只依赖这个接口,不依赖具体实现。这样,新增支付方式就像换零件一样简单。

策略模式的结构

先看一张图,帮你快速建立整体认知。

Context(上下文) 持有策略接口引用 Strategy(策略接口) 定义算法规范 ConcreteStrategyA 支付宝支付 ConcreteStrategyB 微信支付 ConcreteStrategyC 银联支付 可替换 可替换 策略模式核心:上下文持有策略接口,具体策略可自由替换 新增策略只需实现接口,无需修改上下文代码

这张图展示了策略模式的三个核心角色:

  • Context(上下文):持有策略接口的引用,负责调用策略。它不关心具体是哪种策略。
  • Strategy(策略接口):定义算法的规范,所有具体策略都要实现这个接口。
  • ConcreteStrategy(具体策略):实现具体的算法逻辑。每个策略都是独立的。

代码实战:电商支付系统

我们直接上代码。先定义策略接口:

// 支付策略接口
public interface PaymentStrategy {
    // 支付方法,返回支付结果
    PayResult pay(PayRequest request);
}

然后实现三种支付方式:

// 支付宝支付
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public PayResult pay(PayRequest request) {
        // 调用支付宝SDK
        System.out.println("调用支付宝支付接口,金额:" + request.getAmount());
        // 处理签名、回调等逻辑
        return PayResult.success("支付宝支付成功");
    }
}

// 微信支付
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public PayResult pay(PayRequest request) {
        // 调用微信支付SDK
        System.out.println("调用微信支付接口,金额:" + request.getAmount());
        return PayResult.success("微信支付成功");
    }
}

// 银联支付
public class UnionPayStrategy implements PaymentStrategy {
    @Override
    public PayResult pay(PayRequest request) {
        // 调用银联支付接口
        System.out.println("调用银联支付接口,金额:" + request.getAmount());
        return PayResult.success("银联支付成功");
    }
}

接下来是上下文类,也就是订单支付处理器:

// 支付上下文
public class PaymentContext {
    private PaymentStrategy strategy;
    
    // 设置支付策略
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    
    // 执行支付
    public PayResult executePayment(PayRequest request) {
        if (strategy == null) {
            throw new IllegalStateException("请先选择支付方式");
        }
        // 可以在这里添加统一的日志、监控、事务等
        System.out.println("开始支付,订单号:" + request.getOrderId());
        PayResult result = strategy.pay(request);
        System.out.println("支付完成,结果:" + result.getMessage());
        return result;
    }
}

客户端使用:

// 用户选择支付方式
PaymentContext context = new PaymentContext();

// 用户选择支付宝
context.setPaymentStrategy(new AlipayStrategy());
context.executePayment(new PayRequest("2024001", 299.00));

// 用户选择微信
context.setPaymentStrategy(new WechatPayStrategy());
context.executePayment(new PayRequest("2024002", 159.00));

核心要点:PaymentContext 只依赖 PaymentStrategy 接口,不依赖任何具体实现。新增支付方式时,只需要新增一个实现类,不需要修改 PaymentContext 的代码。这就是「开闭原则」的体现。

另一个场景:文件压缩算法

支付方式是一个经典例子,文件压缩是另一个。ZIP、GZIP、RAR,每种压缩算法的实现完全不同。用策略模式来封装,代码会非常干净。

// 压缩策略接口
public interface CompressionStrategy {
    byte[] compress(byte[] data);
    byte[] decompress(byte[] data);
}

// ZIP压缩
public class ZipCompression implements CompressionStrategy {
    @Override
    public byte[] compress(byte[] data) {
        // ZIP压缩实现
        System.out.println("使用ZIP算法压缩");
        return data; // 简化
    }
    
    @Override
    public byte[] decompress(byte[] data) {
        System.out.println("使用ZIP算法解压");
        return data;
    }
}

// GZIP压缩
public class GzipCompression implements CompressionStrategy {
    @Override
    public byte[] compress(byte[] data) {
        System.out.println("使用GZIP算法压缩");
        return data;
    }
    
    @Override
    public byte[] decompress(byte[] data) {
        System.out.println("使用GZIP算法解压");
        return data;
    }
}

我的经验:在实际项目中,策略模式经常和工厂模式搭配使用。工厂负责创建策略对象,上下文只负责调用。这样职责更清晰,测试也更方便。

策略模式的优缺点

优点 缺点
符合开闭原则,新增策略无需修改现有代码 策略类数量会增多,需要管理
避免大量的 if-else 或 switch-case 客户端必须了解不同策略的区别
策略可以独立测试,提高可测试性 策略之间无法共享状态(但可以通过上下文传递)
算法可以复用,不同上下文共享同一策略 如果策略很少变化,可能过度设计

避坑指南

我曾经踩过的坑:有一次,我把策略模式用在了「几乎不会变化」的算法上。结果策略类写了一堆,但实际只用到一个。这就是过度设计。策略模式适合那些「确实会频繁变化」的场景,不要为了用模式而用模式。

另外,要注意策略的生命周期管理。如果策略对象是无状态的(大多数情况),可以复用同一个实例,避免频繁创建对象。如果策略有状态,每次使用需要新建实例。

什么时候用策略模式?

我个人习惯这样判断:

  • 当你有多个相似的算法,且它们会频繁切换时
  • 当你想避免大量的条件判断时
  • 当算法逻辑复杂,需要独立测试和维护时
  • 当算法需要被多个上下文共享时

嗯,说白了,就是当你发现代码里出现「根据类型做不同处理」的逻辑,而且这个类型还在不断增加时,就该考虑策略模式了。

好了,策略模式就讲到这里。记住它的核心:把算法封装成可替换的零件,让上下文只依赖接口。这样你的代码会更灵活,也更经得起业务变化。


公众号:蓝海资料掘金营,微信deep3321