策略模式:定义、结构、实现、与状态模式的区别、优缺点、应用场景

策略模式,说白了就是「把算法或行为封装起来,让它们可以互相替换」。我刚开始接触设计模式时,觉得这玩意儿不就是多态吗?后来在项目中踩过坑才明白——它解决的核心问题,是如何让代码对修改关闭,对扩展开放

你想想看,如果业务逻辑里有一堆 if-else,每次新增一种策略就要改原来的类,那维护起来得多痛苦?策略模式就是帮你把这些「变化的部分」抽出来,变成一个个独立的策略类。

1. 定义与核心思想

策略模式属于行为型设计模式。它定义了一系列算法,把每个算法封装起来,让它们可以互相替换。算法的变化不会影响使用算法的客户端。

我记得有一次做支付系统,对接了微信、支付宝、银联三种支付方式。一开始用 if-else 硬编码,后来新增一个云闪付,改得我头皮发麻。重构为策略模式后,新增支付方式只需要加一个类,再也不用动原来的代码了。

核心原则: 将「变化的部分」与「不变的部分」分离。策略模式把算法本身抽出来,客户端只依赖策略接口,不依赖具体实现。

2. 结构组成

策略模式的结构其实很简单,就三个角色:

  • 策略接口(Strategy): 定义所有策略必须实现的方法。比如一个支付接口,里面有个 pay() 方法。
  • 具体策略(ConcreteStrategy): 实现策略接口,封装具体的算法或行为。比如微信支付策略、支付宝支付策略。
  • 上下文(Context): 持有策略接口的引用,负责调用策略。它不关心具体是哪个策略在干活。

嗯,这里要注意:上下文本身不实现策略,它只是「委托」给策略对象去执行。说白了,上下文就是个调度员。

3. 代码实现

我习惯用支付场景来演示。先看策略接口:

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

然后是两个具体策略:

// 具体策略A:微信支付
public class WechatPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付:" + amount + "元");
        // 实际的微信支付逻辑...
    }
}

// 具体策略B:支付宝支付
public class Alipay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付:" + amount + "元");
        // 实际的支付宝逻辑...
    }
}

接下来是上下文类:

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

    // 可以通过构造器或setter注入策略
    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

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

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

客户端使用:

public class Client {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext(new WechatPay());
        context.executePayment(100.0);  // 微信支付100元

        // 运行时切换策略
        context.setStrategy(new Alipay());
        context.executePayment(200.0);  // 支付宝支付200元
    }
}

你看,客户端只需要知道策略接口,根本不用关心具体实现。新增一种支付方式,只需要加一个类,完全符合开闭原则。

4. 策略模式与状态模式的区别

这个问题面试经常问。我简单说下我的理解:

对比维度 策略模式 状态模式
核心意图 封装算法,让它们互相替换 封装状态,让对象在不同状态下行为不同
谁决定切换 客户端(外部)决定用哪个策略 状态对象自身决定下一个状态
状态数量 策略之间是平等的,没有顺序 状态之间有明确的转换规则
典型场景 支付方式、排序算法、压缩算法 订单状态、工作流、游戏角色状态

说白了,策略模式是「我选你干活」,状态模式是「我根据当前状态自动变」。我曾经在项目中把状态模式误用成策略模式,结果状态转换逻辑散得到处都是,后来重构才改回来。

5. 优缺点分析

优点:

  • 符合开闭原则,新增策略不需要改原有代码
  • 避免大量的 if-else 或 switch-case
  • 策略可以复用,同一个策略可以在不同上下文使用
  • 便于单元测试,每个策略可以单独测试

缺点:

  • 客户端必须了解所有策略的区别,才能选择合适的策略
  • 策略类数量会增多,每个策略一个类
  • 策略之间如果有公共逻辑,处理起来比较麻烦
避坑指南: 我曾经在项目中把策略模式用过头了——一个业务场景只有两种策略,也硬要拆成策略模式。结果代码结构反而更复杂了。记住:模式是用来解决问题的,不是用来炫技的。如果只有两三个分支,用 if-else 反而更清晰。

6. 应用场景

策略模式在实际项目中非常常见。我列几个典型的:

  • 支付系统: 微信、支付宝、银联、云闪付等不同支付方式
  • 排序算法: 快速排序、归并排序、冒泡排序等,客户端根据需要选择
  • 压缩工具: ZIP、GZIP、RAR 等不同压缩算法
  • 促销活动: 满减、折扣、赠品等不同促销策略
  • 数据导出: 导出为 Excel、PDF、CSV 等不同格式

你想想看,这些场景都有一个共同点:行为有多种实现方式,而且未来可能还会增加。这时候用策略模式,就是最自然的选择。

7. 核心知识图谱

下面这张图帮你理清策略模式的核心逻辑:

策略模式核心结构 Context 持有策略接口引用 <<接口>> Strategy + execute() ConcreteStrategyA 实现策略接口 ConcreteStrategyB 实现策略接口 ConcreteStrategyC 实现策略接口 客户端通过上下文调用策略,运行时可以自由切换

从这张图可以看得很清楚:上下文只依赖策略接口,具体策略之间互不依赖。客户端可以自由选择、切换策略,完全不影响其他部分。

我的建议: 刚开始学策略模式时,别急着套用到所有场景。先从消除 if-else 开始,把那些「经常变化」的分支逻辑抽出来。等你对模式熟悉了,自然就知道什么时候该用、什么时候不该用。

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