Java案例如何实现策略模式?

wen java案例 56

深入解析Java策略模式:从理论到实战的完整案例指南

📚 文章导读

章节 内容要点
策略模式概述 定义、核心角色、适用场景
为什么需要策略模式 解决if-else膨胀与OCP原则
传统实现 vs 策略模式 代码对比与痛点分析
Java实战案例 电商促销系统完整代码
进阶技巧 结合枚举、Lambda、Spring容器
常见陷阱与FAQ 问答环节与最佳实践

策略模式究竟是什么?

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

Java案例如何实现策略模式?

核心角色

  • Context(上下文):持有策略对象的引用,负责调用具体策略
  • Strategy(抽象策略):定义所有策略的公共接口
  • ConcreteStrategy(具体策略):实现具体的算法逻辑

经典适用场景

  • 支付方式选择(微信/支付宝/银行卡)
  • 促销折扣计算(满减/打折/返券)
  • 排序算法切换(快速排序/归并排序/冒泡排序)
  • 验证规则组合(邮箱验证/手机号验证/身份证验证)

为什么非要用策略模式?

想象一个电商系统,当用户下单时需要根据会员等级计算最终价格:

// 噩梦般的if-else
public double calculatePrice(String level, double originalPrice) {
    if ("VIP1".equals(level)) {
        return originalPrice * 0.95;
    } else if ("VIP2".equals(level)) {
        return originalPrice * 0.90;
    } else if ("VIP3".equals(level)) {
        return originalPrice * 0.80;
    } else {
        return originalPrice;
    }
}

问题清单:

  • 违反开闭原则(OCP):新增会员等级必须修改核心代码
  • 代码膨胀:随着策略增多,if-else链无限拉长
  • 测试困难:必须重新测试所有分支
  • 复用性差:无法在其他场景直接复用计算逻辑

策略模式如何拯救代码?

传统实现 vs 策略模式对比

维度 传统if-else 策略模式
扩展性 差,修改已有类 优,新增策略类即可
可读性 差,逻辑混杂 优,各策略独立
测试难度 高,全量回归 低,单个策略单独测试
复用性 低,耦合在方法内 高,策略可独立使用

完整Java实战案例:电商促销系统

步骤1:定义抽象策略接口

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

步骤2:实现具体策略

public class VIP1Discount implements DiscountStrategy {
    @Override
    public double applyDiscount(double originalPrice) {
        System.out.println("VIP1会员:95折");
        return originalPrice * 0.95;
    }
}
public class VIP2Discount implements DiscountStrategy {
    @Override
    public double applyDiscount(double originalPrice) {
        System.out.println("VIP2会员:9折");
        return originalPrice * 0.90;
    }
}
public class VIP3Discount implements DiscountStrategy {
    @Override
    public double applyDiscount(double originalPrice) {
        System.out.println("VIP3会员:8折");
        return originalPrice * 0.80;
    }
}
public class NormalDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double originalPrice) {
        System.out.println("普通用户:无折扣");
        return originalPrice;
    }
}

步骤3:创建上下文类

public class OrderContext {
    private DiscountStrategy discountStrategy;
    // 通过构造器或setter注入策略
    public OrderContext(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
    public double computeFinalPrice(double originalPrice) {
        return discountStrategy.applyDiscount(originalPrice);
    }
}

步骤4:客户端使用

public class Client {
    public static void main(String[] args) {
        double price = 1000.0;
        // 策略模式:动态选择算法
        DiscountStrategy strategy = new VIP2Discount();
        OrderContext context = new OrderContext(strategy);
        double finalPrice = context.computeFinalPrice(price);
        System.out.println("最终价格:" + finalPrice);
        // 换策略只需换对象
        context = new OrderContext(new VIP3Discount());
        System.out.println("VIP3最终价格:" + context.computeFinalPrice(price));
    }
}

运行结果:

VIP2会员:9折
最终价格:900.0
VIP3会员:8折
VIP3最终价格:800.0

进阶技巧:让策略模式更强大

技巧1:结合枚举实现策略工厂

public enum DiscountEnum {
    VIP1(new VIP1Discount()),
    VIP2(new VIP2Discount()),
    VIP3(new VIP3Discount()),
    NORMAL(new NormalDiscount());
    private DiscountStrategy strategy;
    DiscountEnum(DiscountStrategy strategy) {
        this.strategy = strategy;
    }
    public DiscountStrategy getStrategy() {
        return strategy;
    }
}
// 使用
DiscountStrategy strategy = DiscountEnum.valueOf("VIP2").getStrategy();

技巧2:基于Lambda的策略模式(Java 8+)

public class LambdaStrategyDemo {
    public static void main(String[] args) {
        double price = 1000.0;
        DiscountStrategy vip2 = p -> {
            System.out.println("Lambda版VIP2折扣");
            return p * 0.90;
        };
        OrderContext context = new OrderContext(vip2);
        System.out.println(context.computeFinalPrice(price));
    }
}

技巧3:结合Spring容器实现自动注入

@Component
public class OrderService {
    @Autowired
    private Map<String, DiscountStrategy> strategyMap; // Spring自动注入所有Strategy
    public double calculatePrice(String level, double price) {
        DiscountStrategy strategy = strategyMap.get(level + "Discount");
        if (strategy == null) {
            throw new IllegalArgumentException("未知会员等级:" + level);
        }
        return strategy.applyDiscount(price);
    }
}

常见陷阱与FAQ问答

❓ 问题1:策略模式和工厂模式有什么区别?

维度 策略模式 工厂模式
目的 封装算法,使其可互换 创建对象,隐藏实例化逻辑
关注点 行为变化 对象创建
典型使用 运行时切换算法 根据条件创建不同对象
组合方式 策略由客户端或上下文选择 工厂负责创建和返回

❓ 问题2:策略模式会导致类爆炸吗?

答: 有可能,当策略数量非常多时,会产生大量策略类,解决方案:

  • 使用内部类或匿名类(小策略)
  • 结合Lambda表达式(函数式接口)
  • 使用枚举实现策略(有限策略集合)

❓ 问题3:什么时候不适用策略模式?

  • 策略数量极少且几乎不变化(如只有两种折扣)
  • 算法逻辑极其简单(如固定折扣比例)
  • 性能要求极端场景(策略调用会引入额外对象开销)

❓ 问题4:策略模式如何保证线程安全?

答: 策略对象通常设计为无状态对象(不包含成员变量),这样可安全共享,如果策略需要包含状态,需考虑:

  • 为每个线程创建独立的策略实例
  • 使用ThreadLocal存储状态
  • 设计不可变策略对象

❓ 问题5:策略模式和状态模式有什么区别?

虽然结构相似,但核心意图不同:

  • 策略模式:关注于算法/行为的可替换性,客户端通常主动选择策略
  • 状态模式:关注于对象状态变化导致行为变化,状态切换由内部逻辑控制

策略模式的正确使用姿势

  1. 识别变化点:找到代码中频繁增删改的算法分支
  2. 抽象为接口:定义统一的策略接口
  3. 封装为类:每个算法独立成一个策略类
  4. 依赖注入:通过构造器、setter或工厂传入策略
  5. 善用现代特性:Java 8 Lambda、Spring容器、枚举工厂

策略模式不是银弹,但在算法需要隔离、扩展和复用的场景下,它是最优雅的解决方案之一,正确的架构设计不应畏惧增加类数量,而应追求职责清晰、易维护、高内聚低耦合的设计。


希望这篇文章能帮你彻底理解策略模式,如果你在项目中遇到具体的实现难题,欢迎在实践中验证这些技巧。

抱歉,评论功能暂时关闭!