这道题表面在问「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 步走法:
- 先讲 4 个优势 → 过基础线;
- 用真实场景(促销 / 支付)演示 if-else 怎么变烂的、策略模式怎么解 → 60 分;
- 最后甩工程判断:「分支少且稳定用
if-else,分支多且常变才上策略模式」——3 个 if-else 我自己也写 if-else → 直接 90 分线。

说到底:设计模式是工具——不是炫技的资本。你能说出“什么时候不该用 X”,比 100 个能背出 X 用法的候选人都值钱。面试官这道题筛的就是这种判断力——背模式背不到、踩坑才能踩到。
最后一句:如果哪天你看老代码看到 100 个微小的策略类——大概率是过去某位“想表演设计模式”的同事留下的债。好工程师不是用最多模式的人——是知道什么时候不用模式的人。
在云栈社区和一线开发者一起聊聊:哪些设计模式你在项目里用过、哪些看到就想删?工程判断力,从来不是背出来的。