【设计模型】策略模式

文章目录


前言

 策略模式,就是把同一件事的不同做法分别封装起来,使用时选择其中一种


电商场景中的例子

以电商中下单时计算优惠后的应付金额为例。假设一个订单每次只能选择一种优惠

  • 优惠方式有:满 300 减 50、满 200 减 30、打九折...
  • 订单原价都是400
  • 得到的应付金额为:350、370、360...

假设商品价格和优惠使用资格已经由服务端确认。先定义一个统一接口,规定所有优惠策略都必须能根据原价计算应付金额:

java 复制代码
public interface DiscountStrategy {
    BigDecimal calculatePayAmount(BigDecimal originalAmount);
}

这个接口只规定需要做什么,具体怎么算,由实现类决定。金额使用 BigDecimal,避免直接使用 double 带来的二进制浮点精度问题。

接着,把两种优惠规则分别写成两个类。

java 复制代码
// 策略一:满 300 减 50
public class FullReductionStrategy implements DiscountStrategy {

    @Override
    public BigDecimal calculatePayAmount(BigDecimal originalAmount) {
        BigDecimal threshold = new BigDecimal("300");
        BigDecimal reduction = new BigDecimal("50");

        // compareTo 返回值 >= 0,表示原价 >= 300
        if (originalAmount.compareTo(threshold) >= 0) {
        //执行减法
            return originalAmount.subtract(reduction);
        }

        // 不满足门槛,按原价支付
        return originalAmount;
    }
}

BigDecimal:double 使用二进制浮点数表示数值,无法精确表示某些十进制小数,因此计算结果可能出现微小误差。

传入 400,返回 350;传入 200,返回 200。满减门槛、减多少钱,都由这个策略负责。

java 复制代码
// 策略二:打九折
public class PercentageDiscountStrategy implements DiscountStrategy {

    @Override
    public BigDecimal calculatePayAmount(BigDecimal originalAmount) {
        return originalAmount
                .multiply(new BigDecimal("0.9"))
                .setScale(2, RoundingMode.HALF_UP);
    }
}

传入 400,返回 360.00。setScale(2, RoundingMode.HALF_UP) 表示按照这个示例约定,保留两位小数、四舍五入。

然后,订单服务通过统一接口调用优惠策略:

java 复制代码
public class OrderService {

    public BigDecimal calculateOrderAmount(
            BigDecimal originalAmount,
            DiscountStrategy strategy) {

        // 订单服务把优惠计算交给传入的策略
        BigDecimal payAmount =
                strategy.calculatePayAmount(originalAmount);

        // 后续可以使用 payAmount 保存订单等
        return payAmount;
    }
}

注意方法参数:

DiscountStrategy strategy

它声明的是接口类型,因此既能接收满减策略,也能接收折扣策略。这体现了 Java 的多态:调用相同的方法,实际执行哪个实现,取决于传入的对象。

实际调用时:

java 复制代码
OrderService orderService = new OrderService();
BigDecimal originalAmount = new BigDecimal("400.00");

// 选择满减策略
BigDecimal amount1 = orderService.calculateOrderAmount(
        originalAmount,
        new FullReductionStrategy()
);
System.out.println(amount1); // 350.00

// 选择九折策略
BigDecimal amount2 = orderService.calculateOrderAmount(
        originalAmount,
        new PercentageDiscountStrategy()
);
System.out.println(amount2); // 360.00

可以把这几部分对应起来看:

代码 在策略模式中的角色 负责什么
DiscountStrategy 策略接口 统一优惠计算方法
FullReductionStrategy 具体策略 实现满减规则
PercentageDiscountStrategy 具体策略 实现折扣规则
OrderService 上下文,即使用策略的业务类 调用策略,完成订单金额计算

实际项目中,怎么根据用户选择找到策略?

例如,可以维护"优惠类型 → 策略对象"的映射:

java 复制代码
Map<String, DiscountStrategy> strategies = Map.of(
        "FULL_REDUCTION", new FullReductionStrategy(),
        "PERCENTAGE_DISCOUNT", new PercentageDiscountStrategy()
);

// 假设服务端校验后,确定本次使用满减优惠
String promotionType = "FULL_REDUCTION";

// 根据类型找到策略对象
DiscountStrategy strategy = strategies.get(promotionType);

if (strategy == null) {
    throw new IllegalArgumentException("不支持的优惠类型");
}

// 统一执行
BigDecimal payAmount =
        orderService.calculateOrderAmount(originalAmount, strategy);

这里,Map 负责找到哪个策略,策略对象负责执行具体算法。策略模式本身并不要求必须使用 Map,简单的选择逻辑也可以。

策略模式的优势

其优势主要体现在后续修改需求时:

  • 增加优惠更方便。 新增"会员打八折",可以增加 MemberDiscountStrategy 并注册到映射中,订单服务的调用逻辑不用改。
  • 修改规则更集中。 满减要改成"满 500 减 80",主要修改满减策略,不需要进入订单服务调整一大段优惠分支。
  • 测试更独立。 可以单独测试满减策略在 299、300、301 元时是否计算正确,不必运行整个下单流程。
相关推荐
譕痕1 小时前
Idea 集成 智能体开发工具“千问(qwen)”
java·ide·spring·语言模型
用户8181870627461 小时前
第27章 消息丢失/重复消费的全链路排查(生产者→Broker→消费者)
java·后端
索隆zoro1 小时前
Army 对 jOOQ
java·后端
二炮手亮子2 小时前
java责任链模式
java
AI深栈2 小时前
第 10 章 · Embedding、VectorStore 与 RAG
java·人工智能
Java内核笔记2 小时前
Spring Boot 4 SSRF 防护源码剖析:InetAddressFilter 挡住内网地址与云元数据
java·后端
白远山2 小时前
健身场馆无人自动化系统实战指南:从架构设计到部署全流程解析
java·开发语言·架构·需求分析
user_admin_god2 小时前
第 09 篇:实践一 —— 文本摘要(Map-Reduce 分块)
java·人工智能·spring boot·语言模型
咖啡八杯2 小时前
Controller 基类封装:BaseController 的模板方法设计
java·架构·设计