如何重构代码以遵循开闭原则?购物车结算场景优化咨询
重构违反开闭原则的购物车结算代码
问题背景
现有多种产品,每种对应不同优惠策略,加入购物车后调用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
相关产品推荐
相关产品推荐

