1. 环境准备:一个真实的嵌套地狱案例
假设你维护一个支付路由服务,现有代码里有这样一段逻辑:根据订单类型、金额和用户等级计算手续费。原始实现用了一堆if-else和switch,层层嵌套,可读性极差。为演示重构,我们先搭建最小Java环境:JDK 11+,任意IDE,以及一个Maven项目。核心类如下:
// OrderType 枚举:NORMAL, PREMIUM, SUBSCRIPTION
// UserLevel 枚举:BRONZE, SILVER, GOLD, PLATINUM
public class FeeCalculator {
public double calculate(Order order) {
double fee = 0.0;
if (order.getType() == OrderType.NORMAL) {
if (order.getAmount() < 100) {
fee = 1.0;
} else {
fee = 0.5;
}
} else if (order.getType() == OrderType.PREMIUM) {
if (order.getUserLevel() == UserLevel.GOLD) {
fee = 0.0;
} else if (order.getUserLevel() == UserLevel.PLATINUM) {
fee = 0.0;
} else {
fee = 0.3;
}
} else if (order.getType() == OrderType.SUBSCRIPTION) {
// 更多嵌套...
fee = 0.2;
}
// 还有更多else if...
return fee;
}
}
这段代码的问题:可读性低 (逻辑散落在大量卫语句中)、违背开闭原则 (新增订单类型需修改现有方法)、测试困难(需构造大量组合条件)。
2. 分步实现:策略模式+工厂方法重构
我们将"计算手续费"抽象为一个策略接口,每种订单类型对应一个实现类,再通过工厂根据订单类型获取策略。
2.1 定义策略接口
public interface FeeStrategy {
double calculate(Order order);
}
2.2 实现具体策略
public class NormalFeeStrategy implements FeeStrategy {
@Override
public double calculate(Order order) {
return order.getAmount() < 100 ? 1.0 : 0.5;
}
}
public class PremiumFeeStrategy implements FeeStrategy {
@Override
public double calculate(Order order) {
UserLevel level = order.getUserLevel();
if (level == UserLevel.GOLD || level == UserLevel.PLATINUM) {
return 0.0;
}
return 0.3;
}
}
public class SubscriptionFeeStrategy implements FeeStrategy {
@Override
public double calculate(Order order) {
return 0.2;
}
}
2.3 创建策略工厂
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FeeStrategyFactory {
private static final Map<OrderType, FeeStrategy> strategies = new ConcurrentHashMap<>();
static {
strategies.put(OrderType.NORMAL, new NormalFeeStrategy());
strategies.put(OrderType.PREMIUM, new PremiumFeeStrategy());
strategies.put(OrderType.SUBSCRIPTION, new SubscriptionFeeStrategy());
}
public static FeeStrategy getStrategy(OrderType type) {
FeeStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("Unsupported order type: " + type);
}
return strategy;
}
}
2.4 重构后的计算器
public class FeeCalculator {
public double calculate(Order order) {
FeeStrategy strategy = FeeStrategyFactory.getStrategy(order.getType());
return strategy.calculate(order);
}
}
现在,FeeCalculator.calculate() 只有两行:获取策略 → 执行计算。新增订单类型时,只需添加新的策略实现类并在工厂注册,无需修改现有代码。
3. 验证效果:可读性与可维护性对比
我们来对比重构前后的代码度量:
- 圈复杂度:重构前方法圈复杂度为 7(多个分支),重构后每个策略类复杂度 ≤ 3,整体平均复杂度从 7 降至 2.5。
- 单测覆盖率 :重构前需模拟所有组合(3种订单 x 4种用户等级 x 2种金额区间 ≈ 24个case),重构后每个策略独立测试,总case数降至每个策略2~4个,且覆盖度更容易达到100%。
- 可读性 :新代码遵循"单一职责",每个策略类只干一件事,命名即文档。后续维护者看到
NormalFeeStrategy就能立刻知道其作用。
此外,若后续需要支持动态费率(如从数据库加载配置),只需在工厂中注入配置即可,策略类保持不变。
4. 总结与扩展
本实战展示了如何用策略模式 + 工厂方法 消除卫语句嵌套,核心收益是:降低圈复杂度、提升可测试性、符合开闭原则。对于更复杂的场景(如策略间有依赖或需要组合),可进一步引入责任链模式 或装饰器模式。建议在团队中推广此类重构,持续改善代码健康度。