策略模式和 if-else 相比有什么好处?


策略模式和 if-else 相比有什么好处?

面试考察点

  1. 痛点认知:面试官想知道你是否遇到过 " if-else 写到几百行,改一处怕牵十处" 的场景,以及你是否能意识到这不是 "代码写得乱" 的问题,而是设计的问题。

  2. 重构能力:能否用代码展示从 if-else 到策略模式的重构过程,证明你不只是背了概念。

  3. 工程判断力:这是加分项——面试官想看你是否知道 "不是所有 if-else 都要用策略模式替代",避免过度设计。

核心答案

先说结论:策略模式相比 if-else4 个核心优势

优势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 个分支、每支就两行代码的场景都要上策略模式,这就属于过度设计了。设计模式是解决问题的工具,不是炫技的资本。

面试高频追问

  1. 策略模式有什么缺点?

    • 类的数量增多(每加一种策略就多一个类)。客户端需要知道有哪些策略可选。不过配合 Spring 注入 + Map<String, Strategy> 可以大幅降低使用复杂度。
  2. 除了策略模式,还有什么方式可以消除 if-else

    • Map<String, Function> 函数式映射、枚举(enum 每个实例实现不同逻辑)、工厂模式 + 多态。策略模式是最经典的方式,但不是唯一的。
  3. 你项目中是怎么用策略模式的?

    • 这个要结合自己项目回答。常见场景:支付方式选择、促销折扣计算、消息推送渠道选择、数据导出格式选择等。

常见面试变体

  • "如何消除业务代码中的大量 if-else?"
  • "什么时候该用策略模式?"
  • "策略模式和枚举消除 if-else 有什么区别?"
  • "你在项目中是怎么用策略模式的?"

记忆口诀

策略 vs if-elseif-else 改一处牵全身,策略模式加一个不动一个。

选择依据:分支少且稳定用 if-else,分支多且常变上策略。

一句话总结:策略模式用 "类的扩展" 替代 "方法的修改",本质是开闭原则的体现。

总结

策略模式相比 if-else 的核心优势是:开闭原则(新增不改旧)、单一职责(每个策略独立)、可独立测试、可复用。面试时先说优势,再用促销系统的例子展示重构前后对比,最后强调 "不是所有 if-else 都要消除"——能说出这句话,面试官就知道你有工程判断力,而不是无脑套模式。