设计模式(二十二)行为型:策略模式详解

设计模式(二十二)行为型:策略模式详解

策略模式(Strategy Pattern)是 GoF 23 种设计模式中最具实用性和广泛影响力的行为型模式之一,其核心价值在于定义一系列算法或行为,并将每个算法封装到独立的类中,使得它们可以相互替换,且算法的变化独立于使用它的客户端。它通过将"算法"与"使用算法的上下文"解耦,实现了行为的动态配置与高度可扩展性。策略模式是构建可配置系统、实现多态行为、支持插件化架构、优化性能选择、实现业务规则引擎、支持 A/B 测试等场景的基石,是将"算法即服务"理念落地的关键设计范式。

一、详细介绍

策略模式解决的是"一个类有多种实现同一功能的算法,且这些算法需要在运行时根据条件动态选择,或需要独立于客户端进行扩展和维护"的问题。在传统设计中,通常使用条件语句(如 if-elseswitch-case)根据配置或参数选择不同的算法分支。这导致:

  • 代码臃肿:所有算法逻辑集中在单一方法或类中。
  • 难以扩展:新增算法需要修改现有代码,违反开闭原则。
  • 难以复用:算法逻辑无法独立复用。
  • 紧耦合:上下文类与具体算法实现紧密耦合。

策略模式的核心思想是:将每个算法封装成一个独立的类(策略类),这些类实现一个共同的策略接口。上下文类(Context)持有对策略接口的引用,通过多态调用算法,而无需知道具体实现。算法的切换通过注入不同的策略对象实现

该模式包含以下核心角色:

  • Context(上下文) :定义客户端使用的接口,包含一个对 Strategy 接口的引用。它将算法相关的请求委托给策略对象执行,而不关心具体实现。
  • Strategy(策略接口) :定义所有具体策略类共有的操作接口(如 execute()calculate())。它声明了算法的抽象行为。
  • ConcreteStrategyA, ConcreteStrategyB, ...(具体策略类) :实现 Strategy 接口,封装具体的算法或行为实现。每个具体策略提供一种算法的完整实现。

策略模式的关键优势:

  • 符合开闭原则:新增算法只需添加新的具体策略类,无需修改上下文或客户端代码。
  • 符合单一职责原则:每个策略类只负责一种算法的实现。
  • 算法可独立复用:策略类可被多个上下文或系统复用。
  • 支持运行时切换:上下文可在运行时动态更换策略对象。
  • 消除条件语句 :将 switch 逻辑转化为对象组合。
  • 易于单元测试:每个策略可独立测试。

与"状态模式"相比,策略模式关注算法的替换 ,状态模式关注状态驱动的行为变化 ;策略通常由客户端或配置决定,状态转换由内部逻辑驱动。与"命令模式"相比,命令封装请求 ,策略封装算法 。与"模板方法模式"相比,模板方法在编译时 通过继承固定算法骨架,策略在运行时通过组合动态选择算法。

策略模式适用于:

  • 多种算法实现同一功能(如排序、压缩、加密、支付方式、运费计算)。
  • 需要运行时动态选择算法。
  • 需要避免庞大的条件分支。
  • 需要独立测试或复用算法。
  • 实现业务规则引擎或配置化行为。

二、策略模式的UML表示

以下是策略模式的标准 UML 类图:
uses implements implements implements Context -strategy: Strategy +setStrategy(strategy: Strategy) +executeStrategy(data: Object) <<interface>> Strategy +execute(data: Object) ConcreteStrategyA +execute(data: Object) ConcreteStrategyB +execute(data: Object) ConcreteStrategyC +execute(data: Object)

图解说明

  • Context 持有对 Strategy 接口的引用。
  • Context 通过 setStrategy() 接受不同的策略实现。
  • ContextexecuteStrategy() 方法将调用委托给当前 Strategyexecute()
  • ConcreteStrategy 实现 execute(),提供具体算法。
  • 客户端通过向 Context 注入不同的 ConcreteStrategy 来改变其行为。

三、一个简单的Java程序实例及其UML图

以下是一个电商系统中运费计算的示例,支持多种运费计算策略(标准、加急、免费)。

Java 程序实例
java 复制代码
// 策略接口:运费计算器
interface ShippingCostStrategy {
    double calculate(double weight, double distance);
}

// 具体策略:标准运费
class StandardShippingStrategy implements ShippingCostStrategy {
    private static final double RATE_PER_KG = 2.5;
    private static final double RATE_PER_KM = 0.1;

    @Override
    public double calculate(double weight, double distance) {
        double cost = (weight * RATE_PER_KG) + (distance * RATE_PER_KM);
        System.out.println("📦 标准运费: " + weight + "kg × $" + RATE_PER_KG + 
                          "/kg + " + distance + "km × $" + RATE_PER_KM + "/km = $" + cost);
        return cost;
    }
}

// 具体策略:加急运费
class ExpressShippingStrategy implements ShippingCostStrategy {
    private static final double BASE_FEE = 15.0;
    private static final double PREMIUM_RATE = 0.5; // per km

    @Override
    public double calculate(double weight, double distance) {
        double cost = BASE_FEE + (distance * PREMIUM_RATE);
        // 重量影响较小,主要按距离和基础费
        System.out.println("⚡ 加急运费: 基础费 $" + BASE_FEE + 
                          " + " + distance + "km × $" + PREMIUM_RATE + "/km = $" + cost);
        return cost;
    }
}

// 具体策略:免费运费(促销活动)
class FreeShippingStrategy implements ShippingCostStrategy {
    @Override
    public double calculate(double weight, double distance) {
        System.out.println("🎁 免费运费: 订单满足促销条件,运费为 $0.00");
        return 0.0;
    }
}

// 上下文:购物车
class ShoppingCart {
    private double totalAmount;
    private double weight;
    private double distance;
    private ShippingCostStrategy shippingStrategy; // 持有策略引用

    public ShoppingCart(double totalAmount, double weight, double distance) {
        this.totalAmount = totalAmount;
        this.weight = weight;
        this.distance = distance;
        // 默认策略:标准运费
        this.shippingStrategy = new StandardShippingStrategy();
        System.out.println("🛒 购物车创建: 金额=$" + totalAmount + 
                          ", 重量=" + weight + "kg, 距离=" + distance + "km");
    }

    // 动态设置运费策略
    public void setShippingStrategy(ShippingCostStrategy strategy) {
        this.shippingStrategy = strategy;
        System.out.println("🔄 运费策略已切换");
    }

    // 计算总费用(商品金额 + 运费)
    public double calculateTotal() {
        double shippingCost = shippingStrategy.calculate(weight, distance);
        double total = totalAmount + shippingCost;
        System.out.println("💰 订单总费用: $" + totalAmount + " + $" + shippingCost + " = $" + total);
        return total;
    }

    // 根据订单金额自动应用免费运费(演示策略选择逻辑)
    public void applyPromotion() {
        if (totalAmount >= 100.0) {
            System.out.println("🎉 订单金额 ≥ $100,应用免费运费促销!");
            setShippingStrategy(new FreeShippingStrategy());
        } else {
            System.out.println("🛒 订单金额 < $100,不满足免费运费条件。");
        }
    }
}

// 客户端使用示例
public class StrategyPatternDemo {
    public static void main(String[] args) {
        System.out.println("🛒 电商运费计算系统 - 策略模式示例\n");

        // 场景1: 普通订单,使用标准运费
        System.out.println("--- 场景1: 普通订单 ($80) ---");
        ShoppingCart cart1 = new ShoppingCart(80.0, 5.0, 200.0);
        cart1.calculateTotal(); // 使用默认标准策略

        System.out.println("\n--- 切换为加急运费 ---");
        cart1.setShippingStrategy(new ExpressShippingStrategy());
        cart1.calculateTotal();

        // 场景2: 大额订单,应用促销
        System.out.println("\n\n--- 场景2: 大额订单 ($120) ---");
        ShoppingCart cart2 = new ShoppingCart(120.0, 3.0, 150.0);
        cart2.applyPromotion(); // 自动应用免费运费
        cart2.calculateTotal();

        System.out.println("\n--- 手动切换回标准运费(演示灵活性)---");
        cart2.setShippingStrategy(new StandardShippingStrategy());
        cart2.calculateTotal();
    }
}
实例对应的UML图(简化版)

uses implements implements implements ShoppingCart -totalAmount: double -weight: double -distance: double -shippingStrategy: ShippingCostStrategy +setShippingStrategy(strategy: ShippingCostStrategy) +calculateTotal() +applyPromotion() <<interface>> ShippingCostStrategy +calculate(weight: double, distance: double) StandardShippingStrategy +calculate(weight: double, distance: double) ExpressShippingStrategy +calculate(weight: double, distance: double) FreeShippingStrategy +calculate(weight: double, distance: double)

运行说明

  • ShoppingCart 是上下文,持有 ShippingCostStrategy 引用。
  • ShippingCostStrategy 定义运费计算接口。
  • 三种具体策略实现不同的计算逻辑。
  • ShoppingCart 通过 setShippingStrategy() 动态更换策略。
  • calculateTotal() 委托给当前策略的 calculate() 方法。
  • applyPromotion() 演示了根据业务规则自动选择策略。

四、总结

特性 说明
核心目的 封装可互换的算法,实现算法与使用的解耦
实现机制 定义策略接口,上下文委托调用具体策略
优点 符合开闭/单一职责原则、消除条件分支、支持运行时切换、算法可复用、易于测试
缺点 增加类数量、客户端需了解策略类型、简单算法可能过度设计
适用场景 多种算法选择、配置化行为、插件化架构、业务规则引擎、A/B测试
不适用场景 算法极少、行为固定、性能极度敏感(虚函数调用开销)

策略模式使用建议

  • 策略类可设计为无状态(推荐),便于共享和线程安全。
  • 可结合"工厂模式"或"依赖注入"创建和注入策略。
  • 在 Java 中,enum 可实现简单策略(每个常量实现接口)。
  • 可结合"组合模式"实现复合策略。

架构师洞见:

策略模式是"关注点分离"与"依赖倒置"原则的典范。在现代架构中,其思想已演变为微服务架构 (每个服务是业务策略的实现)、插件系统 (如 IDE 插件、浏览器扩展)、A/B 测试框架 (不同策略对应不同用户组)、机器学习模型服务 (不同模型作为预测策略)和 云原生配置管理 (不同环境使用不同策略)。例如,Spring 框架的 PasswordEncoderLoadBalancer 都是策略模式的应用;Kubernetes 的调度器可选择不同调度策略;在 AI 推理服务中,不同模型版本作为策略被动态切换。

未来趋势是:策略模式将与Serverless 架构 深度融合,每个函数即一个策略;在边缘计算 中,设备根据网络状况选择数据处理策略;在量子计算 中,不同量子算法作为策略被选择;在元宇宙中,用户交互逻辑可配置为不同策略。

掌握策略模式,是设计灵活、可配置、可演进 系统的必备技能。作为架构师,应在设计任何需要"多选项"、"可配置行为"或"算法替换"的模块时,优先考虑策略模式。它不仅是模式,更是系统适应性的灵魂------它让系统摆脱了硬编码的束缚,具备了在变化的环境中动态选择最优路径的能力,从而构建出能够持续进化、自我优化的智能软件生态系统。

相关推荐
触底反弹3 小时前
🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚!
人工智能·设计模式·面试
晏宁科技YaningAI15 小时前
VoIP系统的工程实现模型:从信令控制到媒体传输的完整架构解析
网络·人工智能·架构·系统架构·信息与通信
尽兴-19 小时前
企业业务系统架构选型与渐进式演进
运维·系统架构·devops
张小姐的猫20 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
杨充1 天前
10.可测试性实战设计
设计模式·开源·代码规范
杨充1 天前
9.重构十二式的实战
设计模式·开源·代码规范
杨充1 天前
6.设计原则的全景图
设计模式·开源·全栈
杨充1 天前
2.面向对象的特性
设计模式
杨充1 天前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充1 天前
8.反模式与坏味道
设计模式·开源·代码规范