1. 为什么我们需要干掉业务代码中的if-else
在业务系统开发中,我们经常会遇到这样的场景:根据不同的条件执行不同的业务逻辑。比如电商系统中的订单处理,可能需要根据不同的支付方式、用户等级、促销活动等条件来执行不同的计算逻辑。新手程序员最直接的做法就是写一堆if-else语句:
java复制if (user.isVIP()) {
// VIP用户逻辑
} else if (order.isGroupBuy()) {
// 团购逻辑
} else if (payment.isWeChatPay()) {
// 微信支付逻辑
} else {
// 默认逻辑
}
这种写法在业务初期可能还能接受,但随着业务复杂度增加,问题会越来越明显:
- 可维护性差:当新增一种业务场景时,需要在原有代码中插入新的条件分支,容易破坏原有逻辑
- 可读性差:当条件分支超过5个时,代码就会变得难以阅读和理解
- 违反开闭原则:每次修改都需要改动原有代码,而不是扩展新代码
- 测试困难:每个条件分支都需要单独的测试用例,分支越多测试越复杂
我在实际项目中见过一个支付处理类,里面有超过20层的if-else嵌套,每次修改都像在走钢丝,稍有不慎就会引入bug。这就是典型的"代码坏味道"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式与工厂模式的黄金组合
2.1 策略模式的核心思想
策略模式(Strategy Pattern)定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
在业务代码中,我们可以把每个条件分支对应的业务逻辑封装成一个独立的策略类。这样:
- 每个策略类只关注自己的业务逻辑
- 策略类之间相互独立,修改一个不会影响其他
- 新增策略只需添加新类,无需修改原有代码
2.2 工厂模式的配合使用
单纯使用策略模式,客户端代码还是需要知道具体使用哪个策略。这时候工厂模式就派上用场了:
- 工厂负责根据条件创建合适的策略对象
- 客户端代码只需要和工厂交互,不需要知道具体策略
- 策略的创建逻辑集中在工厂中,便于管理和修改
这种组合的威力在于:
- 完全消除了业务代码中的条件判断
- 新增业务场景只需添加新策略类,符合开闭原则
- 每个策略类可以独立测试,降低复杂度
- 业务逻辑的组织更加清晰
3. 实战:重构订单折扣计算逻辑
让我们通过一个实际案例来看看如何应用这种模式。假设我们有一个订单系统,需要根据不同的用户类型和促销活动计算最终价格。
3.1 原始if-else实现
java复制public BigDecimal calculatePrice(Order order, User user) {
BigDecimal price = order.getOriginalPrice();
if (user.isVIP()) {
price = price.multiply(BigDecimal.valueOf(0.9));
} else if (order.isGroupBuy()) {
price = price.multiply(BigDecimal.valueOf(0.85));
} else if (order.isFlashSale()) {
price = price.multiply(BigDecimal.valueOf(0.8));
} else if (user.isNewUser() && order.getAmount() > 100) {
price = price.subtract(BigDecimal.valueOf(20));
}
return price;
}
3.2 策略模式重构
首先定义折扣策略接口:
java复制public interface DiscountStrategy {
boolean canApply(Order order, User user);
BigDecimal applyDiscount(Order order, User user);
}
然后实现具体的策略类:
java复制// VIP折扣策略
public class VipDiscountStrategy implements DiscountStrategy {
@Override
public boolean canApply(Order order, User user) {
return user.isVIP();
}
@Override
public BigDecimal applyDiscount(Order order, User user) {
return order.getOriginalPrice().multiply(BigDecimal.valueOf(0.9));
}
}
// 团购折扣策略
public class GroupBuyDiscountStrategy implements DiscountStrategy {
@Override
public boolean canApply(Order order, User user) {
return order.isGroupBuy();
}
@Override
public BigDecimal applyDiscount(Order order, User user) {
return order.getOriginalPrice().multiply(BigDecimal.valueOf(0.85));
}
}
3.3 工厂类实现
java复制public class DiscountStrategyFactory {
private static final List<DiscountStrategy> STRATEGIES = Arrays.asList(
new VipDiscountStrategy(),
new GroupBuyDiscountStrategy(),
new FlashSaleDiscountStrategy(),
new NewUserDiscountStrategy()
);
public static DiscountStrategy getStrategy(Order order, User user) {
return STRATEGIES.stream()
.filter(strategy -> strategy.canApply(order, user))
.findFirst()
.orElse(new DefaultDiscountStrategy());
}
}
3.4 客户端调用
java复制public BigDecimal calculatePrice(Order order, User user) {
DiscountStrategy strategy = DiscountStrategyFactory.getStrategy(order, user);
return strategy.applyDiscount(order, user);
}
现在,代码变得非常简洁,新增折扣策略只需要:
- 创建新的策略类实现DiscountStrategy接口
- 在工厂类中注册新策略
- 原有代码完全不需要修改
4. 高级应用与优化技巧
4.1 策略的优先级管理
在实际业务中,策略之间可能存在优先级关系。比如VIP用户同时参加团购活动,应该优先使用哪个折扣?我们可以在工厂类中通过策略的排序来实现:
java复制public class DiscountStrategyFactory {
private static final List<DiscountStrategy> STRATEGIES = Arrays.asList(
new FlashSaleDiscountStrategy(), // 最高优先级
new GroupBuyDiscountStrategy(),
new VipDiscountStrategy(),
new NewUserDiscountStrategy()
);
// 其他代码不变
}
4.2 组合策略模式
有时候一个订单可能同时满足多个策略条件,我们可以实现组合策略:
java复制public class CompositeDiscountStrategy implements DiscountStrategy {
private final List<DiscountStrategy> strategies;
public CompositeDiscountStrategy(List<DiscountStrategy> strategies) {
this.strategies = strategies;
}
@Override
public boolean canApply(Order order, User user) {
return !getApplicableStrategies(order, user).isEmpty();
}
@Override
public BigDecimal applyDiscount(Order order, User user) {
List<DiscountStrategy> applicableStrategies = getApplicableStrategies(order, user);
BigDecimal price = order.getOriginalPrice();
for (DiscountStrategy strategy : applicableStrategies) {
price = strategy.applyDiscount(order, user);
}
return price;
}
private List<DiscountStrategy> getApplicableStrategies(Order order, User user) {
return strategies.stream()
.filter(strategy -> strategy.canApply(order, user))
.collect(Collectors.toList());
}
}
4.3 使用Spring优化工厂类
在Spring项目中,我们可以利用依赖注入进一步简化工厂类:
java复制@Service
public class DiscountStrategyFactory {
@Autowired
private List<DiscountStrategy> strategies;
public DiscountStrategy getStrategy(Order order, User user) {
return strategies.stream()
.filter(strategy -> strategy.canApply(order, user))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No suitable strategy found"));
}
}
这样新增策略时,只需要创建新的策略类并加上@Component注解,Spring会自动将其注入到工厂中。
4.4 性能优化考虑
当策略数量很多时,每次查找合适的策略可能会有性能问题。可以考虑:
- 使用缓存:缓存策略选择结果
- 预编译策略:在系统启动时预编译策略匹配规则
- 使用规则引擎:对于特别复杂的策略组合,可以考虑引入Drools等规则引擎
5. 实际项目中的经验分享
在多个项目中应用这种模式后,我总结了一些实用经验:
-
策略粒度控制:策略的粒度不宜过细也不宜过粗。通常一个独立的业务规则对应一个策略比较合适。
-
策略命名规范:策略类名应该清晰表达其业务含义,比如"NewUserOver100DiscountStrategy"比"DiscountStrategy1"要好得多。
-
单元测试:每个策略类应该有自己的单元测试,验证其canApply和applyDiscount逻辑。
-
文档注释:为每个策略类添加详细的文档注释,说明其适用场景和业务规则。
-
监控统计:在实际项目中,建议为策略使用情况添加监控,了解各策略的实际使用频率。
-
避免过度设计:对于简单的、不太可能变化的业务规则,使用if-else可能更合适。不要为了模式而模式。
一个常见的陷阱是策略类之间出现重复代码。这时可以考虑:
- 使用抽象基类提取公共逻辑
- 使用组合模式将公共逻辑拆分成独立组件
- 应用模板方法模式
我在一个电商项目中应用这种模式后,订单折扣相关的代码从原来的2000多行缩减到500行左右,而且新增促销活动的时间从原来的1-2天缩短到2-3小时,维护成本大幅降低。
