策略模式(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在软件开发中,我们常常需要根据不同的条件或场景执行不同的逻辑。例如,电商平台的优惠券规则、游戏中的角色攻击方式、甚至是交通信号灯的控制逻辑,这些场景都可能因为条件变化而产生多种行为分支。如何将这些复杂的条件判断逻辑清晰地组织起来,同时保持代码的可维护性和扩展性?这正是 策略模式 的核心价值所在。

策略模式是一种行为型设计模式,它通过封装算法或行为的不同变体,并让这些变体能够在运行时动态切换。本文将从基础概念、实现原理、实际案例及代码示例等角度,逐步解析策略模式的设计思想,帮助开发者掌握这一模式在实际项目中的应用。


策略模式的定义与核心思想

定义

策略模式(Strategy Pattern)允许一个类的行为在运行时根据需要动态选择不同的算法或策略。其核心是将算法的实现与使用算法的客户端解耦,使得算法的变化不会影响客户端代码。

核心思想:“算法家族”与“委托决策”

  1. 算法家族:将所有可能的行为变体抽象为一个共同的接口,每个具体实现类代表一种策略。
  2. 委托决策:客户端不再直接处理复杂的条件判断,而是将决策权交给策略对象,通过组合或依赖注入的方式调用策略。

形象比喻:交通灯的控制逻辑

想象一个十字路口的交通灯控制系统:

  • 红灯:所有车辆停止。
  • 绿灯:直行车辆通行。
  • 左转箭头绿灯:左转车辆通行。

这些不同的规则可以看作是不同的“策略”,而交通灯控制器(客户端)只需要根据当前时间或传感器数据选择对应的策略即可,无需在代码中硬编码复杂的条件分支。


策略模式的实现步骤

以下是实现策略模式的典型步骤:

1. 定义策略接口

首先,定义一个公共接口或抽象类,声明所有策略必须实现的方法。例如:

public interface TrafficLightStrategy {
    void execute();
}

2. 创建具体策略类

为每种行为变体创建具体的实现类:

public class RedLightStrategy implements TrafficLightStrategy {
    @Override
    public void execute() {
        System.out.println("红灯亮起,所有车辆停止!");
    }
}

public class GreenLightStrategy implements TrafficLightStrategy {
    @Override
    public void execute() {
        System.out.println("绿灯亮起,直行车辆通行!");
    }
}

3. 创建上下文(Context)类

上下文类持有策略接口的引用,并提供方法供客户端切换策略:

public class TrafficLightContext {
    private TrafficLightStrategy strategy;

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

    public void executeStrategy() {
        if (strategy != null) {
            strategy.execute();
        }
    }
}

4. 客户端使用策略

在客户端代码中,根据条件动态选择策略:

public class Client {
    public static void main(String[] args) {
        TrafficLightContext context = new TrafficLightContext();
        
        // 根据时间选择策略
        int currentTime = 15; // 15:00
        if (currentTime < 18) {
            context.setStrategy(new GreenLightStrategy());
        } else {
            context.setStrategy(new RedLightStrategy());
        }
        
        context.executeStrategy();
    }
}

策略模式的优势与适用场景

优势分析

  1. 消除条件判断嵌套:将条件分支逻辑集中到策略类中,避免客户端代码中出现大量的 if-elseswitch 语句。
  2. 提高扩展性:新增策略只需实现接口,无需修改现有代码,符合开闭原则(对扩展开放,对修改关闭)。
  3. 提升代码复用性:相同策略可以在不同上下文中复用。
  4. 清晰的职责分离:策略接口与具体实现分离,降低模块间的耦合度。

适用场景

  1. 多条件分支场景:当业务逻辑因不同条件产生多种行为时(如支付方式、排序算法、权限校验等)。
  2. 算法需要频繁切换:例如游戏中的角色攻击模式(近战、远程、魔法攻击)。
  3. 避免多重继承的复杂性:当多个类需要共享不同行为时,策略模式比继承更灵活。

实际案例:电商平台的优惠券系统

问题背景

电商平台的优惠券规则复杂多变,例如:

  • 满减券:订单金额满 300 元减 50 元。
  • 折扣券:订单金额打 8 折。
  • 品类券:指定类目商品 5 折。

若直接在订单计算逻辑中硬编码这些规则,代码将很快变得难以维护。

策略模式解决方案

1. 定义优惠策略接口

public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}

2. 实现具体策略

// 满减策略
public class FullReductionStrategy implements DiscountStrategy {
    private final double threshold;
    private final double discount;

    public FullReductionStrategy(double threshold, double discount) {
        this.threshold = threshold;
        this.discount = discount;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        if (originalPrice >= threshold) {
            return originalPrice - discount;
        }
        return originalPrice;
    }
}

// 折扣策略
public class PercentageDiscountStrategy implements DiscountStrategy {
    private final double percentage;

    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice * (1 - percentage);
    }
}

3. 上下文类与客户端调用

public class OrderContext {
    private DiscountStrategy discountStrategy;

    public void setDiscountStrategy(DiscountStrategy strategy) {
        this.discountStrategy = strategy;
    }

    public double calculateFinalPrice(double originalPrice) {
        if (discountStrategy != null) {
            return discountStrategy.calculateDiscount(originalPrice);
        }
        return originalPrice;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        OrderContext order = new OrderContext();
        
        // 根据用户选择的优惠券类型动态设置策略
        String couponType = "满减券";
        if ("满减券".equals(couponType)) {
            order.setDiscountStrategy(new FullReductionStrategy(300, 50));
        } else if ("折扣券".equals(couponType)) {
            order.setDiscountStrategy(new PercentageDiscountStrategy(0.2));
        }
        
        double finalPrice = order.calculateFinalPrice(400);
        System.out.println("最终价格:" + finalPrice); // 输出:350.0(满减后)
    }
}

策略模式的扩展与注意事项

扩展方向

  1. 工厂模式结合:通过工厂类根据配置动态生成策略实例,例如从数据库或配置文件加载策略类型。
  2. 策略链(Chain of Responsibility):当多个策略需按顺序执行时,可结合责任链模式实现。

注意事项

  1. 避免过度设计:若场景中策略数量极少且固定,使用策略模式可能增加复杂度。
  2. 策略的上下文依赖:某些策略可能需要访问上下文的内部状态,需通过参数传递或接口暴露必要数据。
  3. 性能优化:频繁切换策略可能导致对象创建开销,可通过对象池或缓存优化。

结论

策略模式通过封装行为变体、解耦决策逻辑,为复杂条件分支问题提供了一种优雅的解决方案。它不仅提升了代码的可维护性和扩展性,还降低了业务规则变更对系统的冲击。无论是电商优惠计算、游戏AI行为,还是权限控制,策略模式都能帮助开发者构建更灵活、可扩展的系统。

掌握策略模式的核心思想后,开发者可以进一步探索其他设计模式(如观察者模式、装饰器模式),逐步构建出结构清晰、易于协作的高质量代码体系。

最新发布