找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

6039

积分

0

好友

767

主题
发表于 3 天前 | 查看: 3| 回复: 0

这道题表面在问「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选型决策矩阵

判断标准(一张表)

维度 用 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 分线。

策略模式面试的90分线总结

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

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

云栈社区和一线开发者一起聊聊:哪些设计模式你在项目里用过、哪些看到就想删?工程判断力,从来不是背出来的。




上一篇:MySQL LEFT JOIN 中 ON 和 WHERE 的核心区别:从临时表执行顺序说起
下一篇:WorkSwarm 蜂群办公智能体:多 Agent 如何协作做 PPT、写歌、写代码
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-26 01:07 , Processed in 1.777598 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表