策略模式和 if-else 相比有什么好处?
策略模式和 if-else 相比有什么好处?
面试考察点
-
痛点认知:面试官想知道你是否遇到过 "
if-else写到几百行,改一处怕牵十处" 的场景,以及你是否能意识到这不是 "代码写得乱" 的问题,而是设计的问题。 -
重构能力:能否用代码展示从
if-else到策略模式的重构过程,证明你不只是背了概念。 -
工程判断力:这是加分项——面试官想看你是否知道 "不是所有
if-else都要用策略模式替代",避免过度设计。
核心答案
先说结论:策略模式相比 if-else 有 4 个核心优势。
| 优势 | if-else | 策略模式 |
|---|---|---|
| 新增分支 | 改原有方法,违反开闭原则 | 新增一个策略类,不动原有代码 |
| 单一职责 | 所有逻辑挤在一个方法里 | 每个策略独立一个类,职责单一 |
| 可测试性 | 要构造各种条件测一个方法 | 每个策略类独立测试 |
| 可复用性 | 逻辑绑死在方法中,别处无法复用 | 策略类可以在任何地方注入使用 |
但不是所有 if-else 都需要用策略模式替代。2~3 个简单分支,if-else 就够了;分支多、经常扩展、逻辑复杂时才值得用策略模式。
深度解析
一、if-else 的痛点:用一个真实场景感受
假设你做一个促销系统,运营隔三差五就加新活动类型:
public BigDecimal calculatePrice(String promotionType, BigDecimal price) {
if ("normal".equals(promotionType)) {
// 无优惠
return price;
} else if ("discount".equals(promotionType)) {
// 打八折
return price.multiply(new BigDecimal("0.8"));
} else if ("fullReduction".equals(promotionType)) {
// 满 100 减 20
return price.compareTo(new BigDecimal("100")) >= 0
? price.subtract(new BigDecimal("20"))
: price;
} else if ("buyOneGetOne".equals(promotionType)) {
// 买一送一(买两件只收一件的钱)
return price.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
} else if ("newUser".equals(promotionType)) {
// 新用户首单减 30
return price.subtract(new BigDecimal("30"));
} else if ("vip".equals(promotionType)) {
// VIP 专属七折
return price.multiply(new BigDecimal("0.7"));
}
throw new IllegalArgumentException("未知促销类型:" + promotionType);
}
这才 6 个分支。真实项目中,促销类型可能有 20 多种,每个分支的逻辑不止一行(要查用户等级、查活动库存、计算叠加优惠……)。一个方法写到 500 行不是梦。
痛点来了:
- 运营说加个 "拼团优惠",你得在这个 500 行的方法里找到位置插入新的
else if,祈祷不影响已有逻辑 fullReduction的计算有 bug,你改的时候手一抖把buyOneGetOne的逻辑也改了- 想单独测试 "满减" 的计算逻辑,你得构造参数跑整个方法
- 另一个服务也想用 "打折" 逻辑,只能复制粘贴
二、策略模式重构:逐一解决痛点
// 策略接口
public interface PromotionStrategy {
BigDecimal calculate(BigDecimal price);
}
// 无优惠
@Component("normal")
public class NormalStrategy implements PromotionStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price;
}
}
// 打折
@Component("discount")
public class DiscountStrategy implements PromotionStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
}
// 满减
@Component("fullReduction")
public class FullReductionStrategy implements PromotionStrategy {
@Override
public BigDecimal calculate(BigDecimal price) {
return price.compareTo(new BigDecimal("100")) >= 0
? price.subtract(new BigDecimal("20"))
: price;
}
}
用 Spring 注入策略 Map,业务代码变得极简:
@Service
public class PromotionService {
// Spring 自动把所有 PromotionStrategy 实现注入到 Map 中
// key = Bean 名称,value = 策略实例
@Autowired
private Map<String, PromotionStrategy> strategyMap;
public BigDecimal calculatePrice(String promotionType, BigDecimal price) {
PromotionStrategy strategy = strategyMap.get(promotionType);
if (strategy == null) {
throw new IllegalArgumentException("未知促销类型:" + promotionType);
}
return strategy.calculate(price);
}
}
逐一对比痛点:
痛点 1:新增分支要改原有代码
if-else:在 500 行方法里加else if,违反开闭原则- 策略模式:新增一个
GroupBuyStrategy类,加@Component("groupBuy"),一行原有代码都不用改
痛点 2:改一个分支影响其他分支
if-else:所有逻辑挤在一个方法里,改满减可能误伤打折- 策略模式:每个策略独立一个类,改满减只改
FullReductionStrategy,其他策略完全不受影响
痛点 3:无法独立测试
if-else:要测满减,得跑整个方法- 策略模式:直接
new FullReductionStrategy().calculate(price),独立写单元测试,干净利落
痛点 4:逻辑无法复用
if-else:打折逻辑绑死在方法里,别的地方要用只能复制- 策略模式:注入
DiscountStrategy就能用,天然复用
三、策略模式还带来了额外的好处
除了解决上面的痛点,策略模式还有几个 if-else 给不了的优势:
1. 运行时动态切换
// 根据用户等级动态选择策略
if (userLevel >= 5) {
strategy = new VipStrategy();
} else if (isNewUser) {
strategy = new NewUserStrategy();
} else {
strategy = new NormalStrategy();
}
strategy.calculate(price);
策略可以在运行时任意切换,甚至可以组合使用(策略 A 不满足条件时退化为策略 B)。if-else 做不到这一点。
2. 策略可以持有状态
if-else 的分支逻辑是无状态的。但策略对象可以持有自己的配置:
public class DiscountStrategy implements PromotionStrategy {
private BigDecimal discountRate; // 可配置的折扣率
public DiscountStrategy(BigDecimal discountRate) {
this.discountRate = discountRate;
}
@Override
public BigDecimal calculate(BigDecimal price) {
return price.multiply(discountRate);
}
}
这样同一个打折策略可以有不同的折扣率——VIP 用户七折,普通用户九折,逻辑完全一样,只是参数不同。
3. 策略可以被 Spring 管理
加了 @Component 的策略自动享受 Spring 的生命周期管理——可以注入依赖、可以使用 AOP、可以读取配置。if-else 里的逻辑享受不到这些。
四、别过度设计:什么时候 if-else 就够了
说了这么多策略模式的好处,但面试官可能会追问一个反向问题:什么时候不该用策略模式?
答案是:不是所有 if-else 都是坏味道。
上面的判断标准总结起来就是:分支少且稳定用 if-else,分支多且常变用策略模式。
我之前见过有候选人连一个 3 个分支、每支就两行代码的场景都要上策略模式,这就属于过度设计了。设计模式是解决问题的工具,不是炫技的资本。
面试高频追问
-
策略模式有什么缺点?
- 类的数量增多(每加一种策略就多一个类)。客户端需要知道有哪些策略可选。不过配合 Spring 注入 +
Map<String, Strategy>可以大幅降低使用复杂度。
- 类的数量增多(每加一种策略就多一个类)。客户端需要知道有哪些策略可选。不过配合 Spring 注入 +
-
除了策略模式,还有什么方式可以消除
if-else?Map<String, Function>函数式映射、枚举(enum每个实例实现不同逻辑)、工厂模式 + 多态。策略模式是最经典的方式,但不是唯一的。
-
你项目中是怎么用策略模式的?
- 这个要结合自己项目回答。常见场景:支付方式选择、促销折扣计算、消息推送渠道选择、数据导出格式选择等。
常见面试变体
- "如何消除业务代码中的大量
if-else?" - "什么时候该用策略模式?"
- "策略模式和枚举消除
if-else有什么区别?" - "你在项目中是怎么用策略模式的?"
记忆口诀
策略 vs if-else:if-else 改一处牵全身,策略模式加一个不动一个。
选择依据:分支少且稳定用 if-else,分支多且常变上策略。
一句话总结:策略模式用 "类的扩展" 替代 "方法的修改",本质是开闭原则的体现。
总结
策略模式相比 if-else 的核心优势是:开闭原则(新增不改旧)、单一职责(每个策略独立)、可独立测试、可复用。面试时先说优势,再用促销系统的例子展示重构前后对比,最后强调 "不是所有 if-else 都要消除"——能说出这句话,面试官就知道你有工程判断力,而不是无脑套模式。
