策略模式:让算法像乐高积木一样自由拼插

说实话,我最早接触策略模式时,觉得它就是个「花架子」。直到有一次在支付系统里踩了坑,才真正明白它的价值。那是个电商项目,支付方式从微信、支付宝到银联,每种支付的计算逻辑都不一样。一开始我用 if-else 硬写,结果每次新增支付方式都要改核心代码,改到后面我自己都怕了。

嗯,策略模式就是来解决这个问题的。它把「做什么」和「怎么做」拆开,让算法可以独立变化。你想想看,这不就是现实中的「策略」吗?打仗有作战策略,下棋有棋路策略,代码里也一样。

策略模式的核心三要素

说白了,策略模式就三个角色:

  • 策略接口(Strategy):定义算法的统一规范。比如「支付」这个动作,不管用哪种方式,最终都是要扣钱。
  • 具体策略(ConcreteStrategy):实现策略接口,每个策略封装一种算法。微信支付、支付宝支付各写各的。
  • 上下文(Context):持有策略接口的引用,负责调用策略。它不关心具体怎么算,只负责「用」。

我个人的习惯是,先把策略接口定义清楚,再写具体实现。上下文反而最后写,因为它只是个「调度员」。

支付方式选择:最经典的案例

先看一个支付场景。假设我们要做一个支付模块,支持微信、支付宝、银联三种方式。传统写法是这样的:

// 传统 if-else 写法(不推荐)
public class PaymentService {
    public void pay(String type, double amount) {
        if ("wechat".equals(type)) {
            System.out.println("微信支付:" + amount);
        } else if ("alipay".equals(type)) {
            System.out.println("支付宝支付:" + amount);
        } else if ("unionpay".equals(type)) {
            System.out.println("银联支付:" + amount);
        } else {
            throw new IllegalArgumentException("不支持的支付方式");
        }
    }
}

这种写法有什么问题?我遇到过最头疼的情况:产品经理说「再加个京东支付」。好,我打开 PaymentService,加一个 else if。改完发现微信支付的逻辑也要微调,又得改同一段代码。改来改去,这个类越来越臃肿,测试用例也越来越难写。

用策略模式重构一下:

// 策略接口
public interface PaymentStrategy {
    void pay(double amount);
}

// 具体策略:微信支付
public class WechatPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付:" + amount + "元");
        // 微信特有的签名、回调逻辑
    }
}

// 具体策略:支付宝支付
public class AlipayPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount + "元");
        // 支付宝特有的验签逻辑
    }
}

// 上下文
public class PaymentContext {
    private PaymentStrategy strategy;

    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePay(double amount) {
        strategy.pay(amount);
    }
}

// 使用
public class Client {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext(new WechatPay());
        context.executePay(100.0);
    }
}

核心变化:新增支付方式时,只需要新增一个策略类,不需要改任何已有代码。这就是「开闭原则」——对扩展开放,对修改关闭。

排序算法切换:另一个实战场景

我记得有一次做数据分析平台,用户上传的数据量差异很大。小数据用冒泡排序就够了,大数据必须用快速排序。如果写死一种排序算法,要么小数据浪费性能,要么大数据跑不动。

策略模式正好派上用场:

// 策略接口
public interface SortStrategy {
    void sort(int[] array);
}

// 具体策略:冒泡排序
public class BubbleSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用冒泡排序");
        // 冒泡排序实现...
    }
}

// 具体策略:快速排序
public class QuickSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用快速排序");
        // 快速排序实现...
    }
}

// 上下文
public class SortContext {
    private SortStrategy strategy;

    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public void executeSort(int[] array) {
        strategy.sort(array);
    }
}

// 运行时动态切换
public class Client {
    public static void main(String[] args) {
        SortContext context = new SortContext();
        int[] data = {5, 3, 8, 1, 9};

        if (data.length < 100) {
            context.setStrategy(new BubbleSort());
        } else {
            context.setStrategy(new QuickSort());
        }
        context.executeSort(data);
    }
}

我的经验:策略模式特别适合「算法族」场景。比如压缩算法(zip/gzip/rar)、加密算法(AES/RSA/SM4)、折扣计算(满减/打折/返现)。只要发现代码里有大量 if-else 判断「类型」然后执行不同逻辑,就可以考虑用策略模式。

策略模式的优缺点

优点 缺点
符合开闭原则,新增策略不影响现有代码 策略类数量会增多,需要管理
避免多重条件判断,代码更清晰 客户端必须了解所有策略的区别
算法可以自由切换,运行时动态改变 策略类之间无法共享数据(除非用上下文传递)

避坑指南:我曾经在一个项目中过度使用策略模式,把只有两种分支的逻辑也拆成策略。结果策略类膨胀到几十个,维护起来反而更麻烦。记住:策略模式适合「算法族」或「行为族」,如果只有两三种变化,用简单的 if-else 或者枚举反而更合适。

策略模式的结构图

策略模式结构图 <<接口>> Strategy ConcreteStrategyA 微信支付 ConcreteStrategyB 支付宝支付 ConcreteStrategyC 银联支付 Context 持有 Strategy 引用 实现 调用

什么时候用策略模式?

我总结了几条判断标准:

  1. 代码里有大量 if-else 或 switch-case,而且每个分支做的事情完全不同。
  2. 算法经常变化,或者未来可能增加新的算法。
  3. 算法之间相互独立,不需要共享状态。
  4. 客户端需要动态选择算法,比如根据用户配置或数据量大小。

举个例子,我最近做的一个报表系统,导出格式有 PDF、Excel、CSV。每种格式的生成逻辑完全不同,而且客户经常要求新增格式。用策略模式后,每次新增格式只需要写一个策略类,然后注册到工厂里,主流程一行代码都不用改。

一句话总结:策略模式就是把「怎么做」封装起来,让「谁来做」和「做什么」解耦。它不复杂,但用好了能让代码优雅很多。

嗯,策略模式就聊到这里。记住:设计模式不是银弹,但策略模式绝对是解决「算法族」问题的最优解之一。下次遇到类似的场景,不妨试试看。

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