⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/jingdongmianshinizheixiangmujiu-23-ge-ifelse-fenzhi-yeyaoyongcelvemoshinijuedehelima/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

这道题面试官真正在筛什么

「3 个 if-else 也要用策略模式吗?」——这道京东面试题表面问「该不该上策略模式」——其实在筛 3 个能力:

  • 能不能讲清策略模式真正解决什么问题——不是「让代码看起来像 OOP」,是开闭原则 + 单一职责 + 可测试 + 可复用——光说”代码更整洁”是面试官最讨厌的空话;
  • 会不会用真实业务场景讲清”if-else 怎么变烂的”——光画几个抽象 Type1 / Type2 / Type3 是背书,用促销 / 支付 / 风控这种真实场景演示 才是踩过坑的;
  • 有没有「不该上策略模式」的工程判断力——这是最关键的加分项——面试官最怕招到无脑套设计模式的人——3 个 if-else 也要上、把项目搞成 100 个微小的策略类——这种人接手老项目就是灾难。

按这三档,下面给 30 / 60 / 90 分答案。

30 分答案:背得出策略模式 4 个核心优势

如果只能用 30 秒回答,先列策略模式 vs if-else 的 4 个核心差异:

优势 if-else 策略模式
新增分支 改原方法、违反开闭原则 新加一个策略类、不动原代码
单一职责 所有逻辑挤在一个方法里 每个策略一个独立类
可测试性 要构造各种条件测一个方法 每个策略类独立写单测
可复用性 逻辑绑死在方法里 策略类可在任何地方注入

一句话兜底:策略模式不是”为了模式而模式”——是用「类的扩展」替代「方法的修改」——本质是开闭原则的体现。

但仅答到这 = 30 分线——背得出概念但没踩过坑、面试官还会追问。

60 分答案:用真实业务场景演示 if-else 怎么烂的

到 60 分线,必须用一个真实业务场景讲清”if-else 是怎么从 50 行烂成 500 行的”。

场景:电商促销系统(运营每周加新活动类型)

最初版本只有 3 种促销,一个 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)) {
return price.compareTo(new BigDecimal("100")) >= 0
? price.subtract(new BigDecimal("20"))
: price; // 满 100 减 20
}
throw new IllegalArgumentException("未知促销类型");
}

3 个分支没什么问题——这就是 90 分答案那一节会讲的「不该上策略模式」的场景。

但运营开始隔三差五加活动——半年后这个方法变成这样:

public BigDecimal calculatePrice(String promotionType, BigDecimal price) {
if ("normal".equals(promotionType)) { ... }
else if ("discount".equals(promotionType)) { ... }
else if ("fullReduction".equals(promotionType)) { ... }
else if ("buyOneGetOne".equals(promotionType)) { ... }
else if ("newUser".equals(promotionType)) { ... }
else if ("vip".equals(promotionType)) { ... }
else if ("groupBuy".equals(promotionType)) { ... }
// ... 还有 13 个分支
}

真实项目里:促销类型 20+ 种、每个分支不止一行(要查用户等级、查活动库存、计算叠加优惠),一个方法 500+ 行不是梦。

4 个翻车点全来了:

  • ❌ 加「拼团优惠」要在 500 行方法里找位置插一个 else if——祈祷不影响其他逻辑;
  • ❌ 改 fullReduction 的 bug、手一抖把 buyOneGetOne 也改了;
  • ❌ 想单独测「满减」的计算——要构造一堆参数跑整个方法;
  • ❌ 另一个服务也想用「打折」——只能复制粘贴。

用 Spring 注入解构成策略模式

// 策略接口
public interface PromotionStrategy {
BigDecimal calculate(BigDecimal 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;
}
}

业务代码:

@Service
public class PromotionService {

@Autowired
private Map<String, PromotionStrategy> strategyMap;
// Spring 自动把所有 PromotionStrategy 实现注入这个 Map
// key = Bean 名称,value = 策略实例

public BigDecimal calculatePrice(String promotionType, BigDecimal price) {
PromotionStrategy strategy = strategyMap.get(promotionType);
if (strategy == null) {
throw new IllegalArgumentException("未知促销类型:" + promotionType);
}
return strategy.calculate(price);
}
}

4 个翻车点全消失:

痛点 if-else 处理方式 策略模式处理方式
新增「拼团」 在 500 行方法里加 else if 新加一个 GroupBuyStrategy.java、@Component("groupBuy")——一行原代码不动
改满减 bug 可能误伤打折 只改 FullReductionStrategy、其他文件不受影响
测试满减 跑整个方法 new FullReductionStrategy().calculate(...) 直接测
复用打折 复制粘贴 注入 DiscountStrategy 即可

讲到这就到 60 分线——能用真实场景演示 + 解出 4 个痛点的对应解。

90 分答案:知道何时不该上策略模式

90 分线的关键——策略模式有缺点——光夸不批评 = 没思考过。

缺点 1:类爆炸

每加一种策略多一个类。20 种促销 = 20 个文件——文件树会变长、新人找代码要多一步。

修法:① 给策略类加业务前缀(PromotionDiscountStrategy 而不是 DiscountStrategy);② 同业务的策略放同一个包里,不要散落在 service 各处。

缺点 2:客户端要知道有哪些策略

问题:调用方要传 "discount" 这种字符串——typo 一个字母运行时才报错。

修法:用枚举做策略 key 替代字符串:

public enum PromotionType {
NORMAL("normal"),
DISCOUNT("discount"),
FULL_REDUCTION("fullReduction");
// ...
}

// 调用
strategyMap.get(PromotionType.DISCOUNT.getCode()); // 编译期检查,typo 不了

缺点 3:跨策略的通用逻辑要抽

10 种促销都要先校验用户等级、再校验活动库存——这部分通用逻辑塞到哪?

修法:

抽法 实现
抽象类(推荐) AbstractPromotionStrategy——通用逻辑写在父类、各策略只重写 calculatePrice()
责任链 用一条链把「校验 → 计算」串起来——校验失败直接退出

何时不该上策略模式?

判断标准(一张表):

维度 用 if-else 上策略模式
分支数量 ≤ 3 个 ≥ 5 个
未来扩展性 几年都不会变 经常加新分支
每个分支的代码量 < 5 行 > 20 行
是否需要独立测试 不需要 需要
是否被多处复用 仅一处 多处

我的判断标准(一句话):「分支少且稳定用 if-else,分支多且常变上策略」——这就是3 个 if-else 不该上策略模式的核心理由。

面试中的反问技巧:面试官问「3 个 if-else 该不该用策略模式」——正面答完后反问一句:「这 3 个分支未来会不会扩展?每个分支的代码量大概多少?需不需要独立测试?」——这一反问就比直接答更有专业感——面试官会给你打高分。

直接掉分的几种答法

  • ❌ “策略模式更好,应该用” → -50 分。任何「绝对优先」的答案都暴露没思考过——面试官就在等这种反应。
  • ❌ “3 个分支也要上策略模式(讲了 4 个优势)” → 直接被刷。3 个简单分支的 if-else 是最自然的写法——上策略模式 = 过度设计的活样本。
  • ❌ “if-else 是反模式” → 大学课本水平。if-else 不是反模式——滥用的 if-else 才是反模式——这一字之差是工程素养的分水岭。
  • ❌ 只讲优点不讲缺点 → 顶多 60 分。会不会讲缺点是 90 分线的核心区分。
  • ❌ “策略模式肯定能消灭所有 if-else” → -30 分。if (strategyMap.get(type) == null) 这种 null 检查的 if 改不掉——消灭的是「业务分支」,不是所有 if。

高频追问怎么接

追问 1:策略模式有什么替代方案?

4 种——按推荐度排:

替代方案 适用场景
Map<String, Function> 简单计算逻辑——用 lambda 替代策略类——轻量级首选
枚举(enum) 分支固定、不会扩展——每个枚举实例实现接口方法——最简洁
工厂模式 + 多态 策略需要复杂初始化(注入 dao / 配置)
责任链模式 多个策略按顺序匹配——找到第一个 match 的就用

追问 2:项目里在哪用到了?

这题要结合自己项目讲——常见落地点:

  • 支付方式选择:微信 / 支付宝 / 银联 / 余额——每种一个 PaymentStrategy;
  • 促销折扣计算:满减 / 折扣 / 拼团 / VIP——上面演示的那个;
  • 消息推送渠道:站内信 / 短信 / 邮件 / Push——每种一个 NotifyStrategy;
  • 数据导出格式:Excel / CSV / PDF / JSON——每种一个 ExportStrategy;
  • 风控策略:黑名单 / 速率限制 / 设备指纹 / 行为分析——每种一个 RiskStrategy。

追问 3:策略模式 vs 状态模式有啥区别?

结构上几乎一样——但意图不同:

  • 策略模式:让客户端选策略——「我要打八折」 / 「我要满减」——主动决定;
  • 状态模式:对象内部状态变化导致行为变化——订单从「待支付」 → 「已支付」 → 「已发货」——对象自动迁移。

记忆口诀:策略是给的、状态是变的。

追问 4:策略类一直在新增、文件树越来越长怎么办?

3 个办法:

  • 按业务模块分包:promotion/strategy/、payment/strategy/ 而不是 strategy/ 大杂烩;
  • 配置化策略:把简单计算逻辑(如折扣率)抽到配置文件 / 数据库,用一个通用 ConfigDrivenStrategy 替代多个简单类;
  • 分层抽象:基类抽公共逻辑、子类只写差异——减少类的总数。

我的判断

这道题答到 30 分容易、答到 60 分要会用真实业务讲、答到 90 分靠工程判断力。

面试时 3 步走法:

  1. 先讲 4 个优势 → 过基础线;
  2. 用真实场景(促销 / 支付)演示 if-else 怎么变烂的、策略模式怎么解 → 60 分;
  3. 最后甩工程判断:「分支少且稳定用 if-else,分支多且常变才上策略模式」——3 个 if-else 我自己也写 if-else → 直接 90 分线。

说到底:设计模式是工具——不是炫技的资本。你能说出”什么时候不该用 X”——比 100 个能背出 X 用法的候选人都值钱。面试官这道题筛的就是这种判断力——背模式背不到、踩坑才能踩到。

最后一句:如果哪天你看老代码看到 100 个微小的策略类——大概率是过去某位”想表演设计模式”的同事留下的债。好工程师不是用最多模式的人——是知道什么时候不用模式的人。

文章目录
  1. 1. 这道题面试官真正在筛什么
  2. 2. 30 分答案:背得出策略模式 4 个核心优势
  3. 3. 60 分答案:用真实业务场景演示 if-else 怎么烂的
    1. 3.0.1. 场景:电商促销系统(运营每周加新活动类型)
    2. 3.0.2. 用 Spring 注入解构成策略模式
  • 4. 90 分答案:知道何时不该上策略模式
    1. 4.0.1. 缺点 1:类爆炸
    2. 4.0.2. 缺点 2:客户端要知道有哪些策略
    3. 4.0.3. 缺点 3:跨策略的通用逻辑要抽
    4. 4.0.4. 何时不该上策略模式?
  • 5. 直接掉分的几种答法
  • 6. 高频追问怎么接
    1. 6.0.1. 追问 1:策略模式有什么替代方案?
    2. 6.0.2. 追问 2:项目里在哪用到了?
    3. 6.0.3. 追问 3:策略模式 vs 状态模式有啥区别?
    4. 6.0.4. 追问 4:策略类一直在新增、文件树越来越长怎么办?
  • 7. 我的判断