前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、策略模式(Strategy Pattern)

1、模式概述

策略模式 是一种 行为型设计模式,它定义一系列 算法或行为,并将每一个算法封装起来,使它们可以 相互替换。策略模式让算法的变化 独立于使用它的客户端

核心思想“同一接口,多种实现 —— 行为可插拔。”

策略模式 = 行为抽象 + 实现分离

  • 它不是“写死逻辑”,而是“把变化的行为抽出来”,让系统更具弹性。

2、使用场景

策略模式适用于以下情况:

  • 一个系统需要在运行时 动态选择多种算法之一
  • 多个类 仅在行为上不同,其余结构高度相似;
  • 需要 避免使用多重条件判断语句(如 if-elseswitch);
  • 希望 将行为逻辑从主业务中解耦,提高可测试性与可维护性。

典型例子

  • 电商订单计算运费(普通快递 / 顺丰 / 自提);
  • 游戏角色攻击方式(近战 / 远程 / 魔法);
  • 数据排序策略(升序 / 降序 / 自定义规则)。

在实际开发中:

  • SpringHandlerMapping 使用策略选择处理器;
  • java.util.Comparator 是策略模式的标准库实现;
  • 任何“同名不同行为”的场景,都是策略模式的用武之地。

3、示例程序:四则运算策略

  • 客户端 直接或通过 Context 使用策略
  • 策略之间 完全独立,互不影响;
  • 新增策略 无需修改原有代码(符合开闭原则)。
角色 职责 示例
Strategy(策略接口) 定义所有支持算法的公共接口 Strategy
ConcreteStrategy(具体策略) 实现 Strategy 接口,提供具体算法 AddStrategy, SubStrategy
Context(上下文) 持有 Strategy 引用,供客户端调用(可选但推荐) 可封装策略切换逻辑
Client(客户端) 选择并使用具体策略 Main

1)策略接口 Strategy

/**
 * 策略接口 —— 定义计算行为
 */
public interface Strategy {
    String calculate(int a, int b);
}

2)具体策略实现

a、加法策略

public class AddStrategy implements Strategy {
    @Override
    public String calculate(int a, int b) {
        return "a + b = " + (a + b);
    }
}

b、减法策略

public class SubStrategy implements Strategy {
    @Override
    public String calculate(int a, int b) {
        return "a - b = " + (a - b);
    }
}

c、乘法策略

public class MulStrategy implements Strategy {
    @Override
    public String calculate(int a, int b) {
        return "a * b = " + (a * b);
    }
}

3)上下文封装(推荐增强版)

虽然策略模式不要求必须有 Context,但引入它可以 隐藏策略创建与切换细节

import java.util.HashMap;
import java.util.Map;

/**
 * 计算上下文 —— 封装策略选择逻辑
 */
public class Calculator {
    
    private final Map<String, Strategy> strategies = new HashMap<>();

    public Calculator() {
        // 注册策略
        strategies.put("add", new AddStrategy());
        strategies.put("sub", new SubStrategy());
        strategies.put("mul", new MulStrategy());
    }

    public String execute(String operation, int a, int b) {
        Strategy strategy = strategies.get(operation);
        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported operation: " + operation);
        }
        return strategy.calculate(a, b);
    }
}

4)客户端测试

a、基础用法(直接使用策略)

public class Main {
    public static void main(String[] args) {
        int a = 10, b = 5;

        System.out.println(new AddStrategy().calculate(a, b));
        System.out.println(new SubStrategy().calculate(a, b));
        System.out.println(new MulStrategy().calculate(a, b));
    }
}

b、推荐用法(通过 Context)

优势体现

  • 客户端无需 new 具体策略类;
  • 策略可通过配置、枚举或工厂动态加载。
public class MainWithContext {
    public static void main(String[] args) {
        Calculator calc = new Calculator();

        System.out.println(calc.execute("add", 10, 5)); // a + b = 15
        System.out.println(calc.execute("sub", 10, 5)); // a - b = 5
        System.out.println(calc.execute("mul", 10, 5)); // a * b = 50
    }
}

输出:

a + b = 15
a - b = 5
a * b = 50

4、 FAQ

1)优点

  • 算法可自由切换:运行时动态选择;
  • 避免条件分支:消除 if-else/switch,提升可读性;
  • 扩展性强:新增策略只需实现接口;
  • 符合开闭原则:对扩展开放,对修改关闭。

2)注意事项

  • 类数量增加:每个策略对应一个类(若策略简单,可考虑 Lambda 表达式简化);
  • 客户端需知道所有策略(除非使用 Context 封装);
  • 策略间无法共享状态(若需状态,考虑结合状态模式)。