You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构代码以遵循开闭原则?购物车结算场景优化咨询

重构违反开闭原则的购物车结算代码

问题背景

现有多种产品,每种对应不同优惠策略,加入购物车后调用checkout()方法计算总价totalPrice和积分loyaltyPoints。但新增带新优惠策略的产品时,必须在checkout()中添加新的else if分支,违反开闭原则(对扩展开放,对修改关闭)。

原代码如下:

public Order checkout() {
    double totalPrice = 0;
    Map<String,Integer> buy2Plus1=new HashMap<>();
    int loyaltyPointsEarned = 0;
    for (Product product : products) {
        double discount = 0;
        if (product.getProductCode().startsWith("DIS_10")) {
            discount = (product.getPrice() * 0.1);
            loyaltyPointsEarned += (product.getPrice() / 10);
        } else if (product.getProductCode().startsWith("DIS_15")) {
            discount = (product.getPrice() * 0.15);
            loyaltyPointsEarned += (product.getPrice() / 15);
        }else if(product.getProductCode().startsWith("DIS_20")){
            discount=(product.getPrice()*0.2);
            loyaltyPointsEarned+=(product.getPrice()/20);
         }else if(product.getProductCode().startsWith("Buy2Plus1")){
            if(buy2Plus1.containsKey(product.getProductCode())){
                buy2Plus1.put(product.getProductCode(), buy2Plus1.get(product.getProductCode())+1);
            }
            else{
                buy2Plus1.put(product.getProductCode(),1);
            }
            if(buy2Plus1.get(product.getProductCode())%3==0){
                discount+=product.getPrice();
                continue;
            }
            loyaltyPointsEarned+=(product.getPrice()/5);
        }else {
            loyaltyPointsEarned += (product.getPrice() / 5);
        }
        totalPrice += product.getPrice() - discount;
    }

    return new Order(totalPrice, loyaltyPointsEarned);
}

重构方案:使用策略模式

通过策略模式将优惠逻辑与结算逻辑解耦,新增策略无需修改原有结算代码,完全符合开闭原则。

步骤1:定义优惠策略接口与上下文类

抽象优惠策略的核心行为,同时用上下文类维护结算过程中的状态(总价、积分、商品计数等):

// 优惠策略接口
public interface DiscountStrategy {
    void apply(Product product, CheckoutContext context);
}

// 结算上下文,传递结算状态
public class CheckoutContext {
    private double totalPrice;
    private int loyaltyPoints;
    private Map<String, Integer> productCountMap = new HashMap<>();

    public void addTotalPrice(double amount) {
        this.totalPrice += amount;
    }

    public void addLoyaltyPoints(int points) {
        this.loyaltyPoints += points;
    }

    public int getProductCount(String productCode) {
        return productCountMap.getOrDefault(productCode, 0);
    }

    public void incrementProductCount(String productCode) {
        productCountMap.put(productCode, getProductCount(productCode) + 1);
    }

    // getter方法
    public double getTotalPrice() { return totalPrice; }
    public int getLoyaltyPoints() { return loyaltyPoints; }
}

步骤2:实现具体优惠策略

为每种优惠规则创建独立类,专注单一逻辑:

1. 百分比折扣策略(通用型)

public class PercentageDiscountStrategy implements DiscountStrategy {
    private double discountRate;
    private int pointsPerUnitPrice;

    public PercentageDiscountStrategy(double discountRate, int pointsPerUnitPrice) {
        this.discountRate = discountRate;
        this.pointsPerUnitPrice = pointsPerUnitPrice;
    }

    @Override
    public void apply(Product product, CheckoutContext context) {
        double discount = product.getPrice() * discountRate;
        context.addTotalPrice(product.getPrice() - discount);
        context.addLoyaltyPoints((int) (product.getPrice() / pointsPerUnitPrice));
    }
}

2. 买二送一策略

public class Buy2Get1FreeStrategy implements DiscountStrategy {
    @Override
    public void apply(Product product, CheckoutContext context) {
        String productCode = product.getProductCode();
        context.incrementProductCount(productCode);
        int count = context.getProductCount(productCode);

        // 每3件免第3件费用
        if (count % 3 != 0) {
            context.addTotalPrice(product.getPrice());
        }
        // 积分规则:每5元积1分
        context.addLoyaltyPoints((int) (product.getPrice() / 5));
    }
}

3. 默认无优惠策略

public class DefaultStrategy implements DiscountStrategy {
    @Override
    public void apply(Product product, CheckoutContext context) {
        context.addTotalPrice(product.getPrice());
        context.addLoyaltyPoints((int) (product.getPrice() / 5));
    }
}

步骤3:策略工厂类

创建工厂类统一管理策略匹配,避免硬编码判断:

public class DiscountStrategyFactory {
    private static final Map<String, DiscountStrategy> STRATEGY_MAP = new HashMap<>();

    // 静态初始化策略映射
    static {
        STRATEGY_MAP.put("DIS_10", new PercentageDiscountStrategy(0.1, 10));
        STRATEGY_MAP.put("DIS_15", new PercentageDiscountStrategy(0.15, 15));
        STRATEGY_MAP.put("DIS_20", new PercentageDiscountStrategy(0.2, 20));
        STRATEGY_MAP.put("Buy2Plus1", new Buy2Get1FreeStrategy());
    }

    public static DiscountStrategy getStrategy(Product product) {
        String productCode = product.getProductCode();
        // 根据商品编码前缀匹配策略
        for (String key : STRATEGY_MAP.keySet()) {
            if (productCode.startsWith(key)) {
                return STRATEGY_MAP.get(key);
            }
        }
        return new DefaultStrategy();
    }
}

步骤4:重构checkout()方法

结算方法简化为遍历商品、调用对应策略,新增策略无需修改此方法:

public Order checkout() {
    CheckoutContext context = new CheckoutContext();
    for (Product product : products) {
        DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(product);
        strategy.apply(product, context);
    }
    return new Order(context.getTotalPrice(), context.getLoyaltyPoints());
}

重构后的类结构

  • DiscountStrategy:抽象策略接口,定义优惠执行方法
  • 具体策略类:PercentageDiscountStrategy、Buy2Get1FreeStrategy、DefaultStrategy,实现接口封装各优惠逻辑
  • CheckoutContext:维护结算过程中的状态数据
  • DiscountStrategyFactory:负责策略的匹配与分发
  • Cart:包含重构后的checkout()方法,依赖策略工厂完成结算
  • Product/Order:原有实体类无需修改

重构优势

  • 符合开闭原则:新增优惠策略仅需添加策略类并在工厂注册,无需修改原有代码
  • 代码解耦:优惠逻辑与结算逻辑分离,每个策略类职责单一,便于维护和单元测试
  • 扩展性强:可轻松实现复杂优惠规则(如满减、阶梯折扣),仅需实现DiscountStrategy接口

内容的提问来源于stack exchange,提问作者Abner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:30:54