Leather Dress Collection 代码重构展示:将冗长Java代码优化为优雅设计模式
Leather Dress Collection 代码重构展示将冗长Java代码优化为优雅设计模式最近在整理一些老项目翻出来一段典型的“祖传代码”。这段代码功能是完整的但读起来就像在走迷宫维护起来更是让人头疼。正好我们可以用它来展示一下如何运用设计模式的思想将一段结构混乱的业务代码重构得清晰、优雅且易于维护。今天要处理的代码是一个简单的订单价格计算器。它根据不同的用户类型普通用户、VIP用户、内部员工和不同的商品类型普通商品、折扣商品、秒杀商品应用不同的折扣策略。原始版本把所有逻辑都塞在了一个巨大的if-else块里我们一起来看看如何“拯救”它。1. 原始代码一个经典的“坏味道”案例我们先看看这段需要被重构的代码。为了聚焦问题我简化了一些细节但核心的“坏味道”都保留了下来。public class PriceCalculator { public double calculatePrice(String userType, String productType, double originalPrice) { double discount 0.0; double finalPrice originalPrice; // 根据用户类型计算折扣 if (VIP.equals(userType)) { if (DISCOUNT.equals(productType)) { discount 0.20; // VIP买折扣商品额外折上折 } else if (SEC_KILL.equals(productType)) { discount 0.10; // VIP买秒杀商品也有小折扣 } else { discount 0.15; // VIP买普通商品 } } else if (INTERNAL.equals(userType)) { discount 0.30; // 内部员工统一折扣 } else { // 普通用户 if (DISCOUNT.equals(productType)) { discount 0.10; } else if (SEC_KILL.equals(productType)) { discount 0.05; } // 普通商品无折扣 } finalPrice originalPrice * (1 - discount); // 一些额外的费用规则同样混乱 double extraFee 0.0; if (INTERNAL.equals(userType)) { extraFee 0; // 内部员工免运费 } else { if (finalPrice 50) { extraFee 5.0; // 小单运费 } if (SEC_KILL.equals(productType)) { extraFee 2.0; // 秒杀商品加收服务费 } } return finalPrice extraFee; } }这段代码的问题非常典型可读性差多层嵌套的if-else语句理解业务逻辑需要在大脑里层层解析。可维护性低如果想增加一种新的用户类型比如“超级VIP”或者新的商品类型必须深入这个庞大的方法内部修改很容易引入错误。可测试性差由于所有逻辑耦合在一起很难为每一种具体的折扣组合编写独立的单元测试。违反开闭原则对扩展开放对修改关闭。这里每次修改都意味着要修改这个核心方法。我们的目标就是把这些纠缠在一起的逻辑用设计模式梳理开来。2. 重构之旅识别模式与解耦逻辑面对这样的代码第一步不是直接动手改而是分析。我们发现折扣的计算逻辑和额外费用的计算逻辑都可以根据“用户类型”和“商品类型”这两个维度进行抽象。这正好是策略模式Strategy Pattern的用武之地。我们可以为不同的折扣策略和费用策略定义独立的算法族。同时创建这些策略对象的过程如果变得复杂可以用工厂模式Factory Pattern来封装让客户端代码更简洁。2.1 第一步定义折扣策略接口与实现我们把折扣计算这部分单独抽出来。// 折扣策略接口 public interface DiscountStrategy { double calculateDiscount(String productType, double originalPrice); } // VIP用户折扣策略 public class VipDiscountStrategy implements DiscountStrategy { Override public double calculateDiscount(String productType, double originalPrice) { switch (productType) { case DISCOUNT: return originalPrice * 0.20; case SEC_KILL: return originalPrice * 0.10; default: // 普通商品 return originalPrice * 0.15; } } } // 内部员工折扣策略 public class InternalStaffDiscountStrategy implements DiscountStrategy { Override public double calculateDiscount(String productType, double originalPrice) { // 内部员工统一折扣与商品类型无关 return originalPrice * 0.30; } } // 普通用户折扣策略 public class RegularUserDiscountStrategy implements DiscountStrategy { Override public double calculateDiscount(String productType, double originalPrice) { switch (productType) { case DISCOUNT: return originalPrice * 0.10; case SEC_KILL: return originalPrice * 0.05; default: return 0.0; // 普通商品无折扣 } } }看这样一来每种用户类型的折扣逻辑都被封装在了自己的类里清晰独立。如果要加一个“超级VIP”只需要新建一个SuperVipDiscountStrategy类即可。2.2 第二步定义额外费用策略接口与实现同样地我们把令人头疼的额外费用规则也抽离出来。// 额外费用策略接口 public interface ExtraFeeStrategy { double calculateExtraFee(String productType, double finalPrice); } // 内部员工费用策略免运费等 public class InternalStaffFeeStrategy implements ExtraFeeStrategy { Override public double calculateExtraFee(String productType, double finalPrice) { return 0.0; // 内部员工全免 } } // 普通用户费用策略 public class RegularUserFeeStrategy implements ExtraFeeStrategy { Override public double calculateExtraFee(String productType, double finalPrice) { double fee 0.0; if (finalPrice 50) { fee 5.0; } if (SEC_KILL.equals(productType)) { fee 2.0; } return fee; } }2.3 第三步创建策略工厂现在我们有了一堆策略类需要一个地方来管理它们根据传入的类型返回正确的策略对象。这里我们用简单的工厂模式。// 策略工厂 public class StrategyFactory { // 获取折扣策略 public static DiscountStrategy getDiscountStrategy(String userType) { switch (userType) { case VIP: return new VipDiscountStrategy(); case INTERNAL: return new InternalStaffDiscountStrategy(); case REGULAR: default: return new RegularUserDiscountStrategy(); } } // 获取费用策略 public static ExtraFeeStrategy getExtraFeeStrategy(String userType) { if (INTERNAL.equals(userType)) { return new InternalStaffFeeStrategy(); } else { return new RegularUserFeeStrategy(); } } }工厂类把对象创建的细节隐藏了起来。未来如果创建逻辑变复杂比如需要从配置读取或使用原型模式也只需要修改这个工厂类。2.4 第四步重构核心计算器最后我们来看重构后的PriceCalculator现在它变得多么清爽。public class RefactoredPriceCalculator { public double calculatePrice(String userType, String productType, double originalPrice) { // 1. 通过工厂获取策略 DiscountStrategy discountStrategy StrategyFactory.getDiscountStrategy(userType); ExtraFeeStrategy extraFeeStrategy StrategyFactory.getExtraFeeStrategy(userType); // 2. 应用折扣策略 double discountAmount discountStrategy.calculateDiscount(productType, originalPrice); double priceAfterDiscount originalPrice - discountAmount; // 3. 应用费用策略 double extraFee extraFeeStrategy.calculateExtraFee(productType, priceAfterDiscount); // 4. 返回最终价格 return priceAfterDiscount extraFee; } }3. 重构效果对比从混乱到清晰我们把新旧两个版本放在一起对比一下变化一目了然。对比维度原始版本 (PriceCalculator)重构版本 (RefactoredPriceCalculator)核心方法行数约40行包含所有业务逻辑约10行只负责流程编排可读性需要仔细阅读嵌套条件才能理解像读说明书获取策略 - 计算折扣 - 计算费用 - 返回结果可维护性修改任意规则需深入核心方法风险高修改规则只需调整对应的策略类核心流程不动可测试性难以测试单一场景需构造复杂参数每个策略类可独立进行单元测试核心计算器只需Mock策略扩展性新增类型必须修改核心方法违反开闭原则新增类型只需增加新的策略类并在工厂注册符合开闭原则职责单一一个类负责了折扣计算、费用计算、逻辑分发每个类职责清晰计算器编排、工厂创建、策略实现最直观的感受是阅读重构后的代码你几乎不需要注释就能明白它在做什么。而原始的代码即便加上注释随着后续修改注释也很容易过时导致代码重新变得难以理解。4. 更进一步使用枚举与注册表优化工厂上面的工厂使用switch语句已经不错了。但我们可以再进一步消除switch让扩展更加优雅。这里可以引入一个策略注册表的概念。// 1. 使用枚举定义用户类型 public enum UserType { REGULAR, VIP, INTERNAL } // 2. 策略注册表这里用Map模拟 public class StrategyRegistry { private static final MapUserType, DiscountStrategy discountStrategyMap new HashMap(); private static final MapUserType, ExtraFeeStrategy extraFeeStrategyMap new HashMap(); static { // 初始化注册关系 discountStrategyMap.put(UserType.VIP, new VipDiscountStrategy()); discountStrategyMap.put(UserType.INTERNAL, new InternalStaffDiscountStrategy()); discountStrategyMap.put(UserType.REGULAR, new RegularUserDiscountStrategy()); extraFeeStrategyMap.put(UserType.INTERNAL, new InternalStaffFeeStrategy()); extraFeeStrategyMap.put(UserType.VIP, new RegularUserFeeStrategy()); // VIP和普通用户费用策略相同 extraFeeStrategyMap.put(UserType.REGULAR, new RegularUserFeeStrategy()); } public static DiscountStrategy getDiscountStrategy(UserType userType) { return discountStrategyMap.get(userType); } public static ExtraFeeStrategy getExtraFeeStrategy(UserType userType) { return extraFeeStrategyMap.get(userType); } // 允许运行时动态注册新策略如果需要 public static void registerDiscountStrategy(UserType userType, DiscountStrategy strategy) { discountStrategyMap.put(userType, strategy); } }然后计算器和使用方式也变得更类型安全public class EnhancedPriceCalculator { public double calculatePrice(UserType userType, String productType, double originalPrice) { DiscountStrategy discountStrategy StrategyRegistry.getDiscountStrategy(userType); ExtraFeeStrategy extraFeeStrategy StrategyRegistry.getExtraFeeStrategy(userType); double discountAmount discountStrategy.calculateDiscount(productType, originalPrice); double priceAfterDiscount originalPrice - discountAmount; double extraFee extraFeeStrategy.calculateExtraFee(productType, priceAfterDiscount); return priceAfterDiscount extraFee; } } // 使用示例 public class Main { public static void main(String[] args) { EnhancedPriceCalculator calculator new EnhancedPriceCalculator(); double price calculator.calculatePrice(UserType.VIP, DISCOUNT, 100.0); System.out.println(Final Price: price); // 输出: Final Price: 83.0 } }这样做的好处是彻底消除了工厂方法中的switch或if-else。新增一种用户类型时只需创建新的枚举值并在注册表中绑定对应的策略即可扩展点非常明确。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。