LLD 和 OOD 编码面试怎么准备:从购物车、权限系统到告警引擎

LLD 和 OOD 编码面试怎么准备:从购物车、权限系统到告警引擎

很多工程师刷了大量算法题,却在 LLD 或 OOD 编码面试里表现不稳定。原因是这类面试不只考“能不能写出函数”,还考你能不能把一个小系统拆成合理的数据模型、API、状态转移和测试用例。

近年的面试里,小型系统实现题很常见:购物车折扣、文件系统、资源权限、告警执行引擎、棋类游戏、日志统计、任务调度器的简化版本。它们看起来不像传统 LeetCode,但底层考察点很集中。

先识别题目的核心状态

LLD 面试的第一步不是写 class,而是找状态。

例如购物车系统里,核心状态可能是:

  • 当前 cart 里有哪些 recipe
  • 每种 ingredient 当前数量
  • 每种 ingredient 对应的 discount tier
  • 当前总折扣
  • undo stack 和 redo stack

资源权限系统里,核心状态可能是:

  • user 到 role 的映射
  • role 到 permission 的映射
  • resource 的层级关系
  • permission 是否继承
  • deny 和 allow 的优先级

告警引擎里,核心状态可能是:

  • alert 配置
  • 每个 alert 上次执行时间
  • 当前状态是 PASS 还是 CRITICAL
  • 上次通知时间
  • repeat interval

面试官希望看到你能把业务描述转成可维护状态,而不是把所有逻辑塞进一个函数。

API 设计要主动说明语义

小系统题通常会给几个 API,比如 add、remove、get_total、undo、redo。你需要主动确认:

  • 重复添加是 no-op 还是报错?
  • 删除不存在对象时怎么处理?
  • 返回值要不要表示操作是否成功?
  • undo 一个 no-op 操作是否应该入栈?
  • redo 在新操作后是否清空?
  • 查询接口是否必须 O(1)?

这些问题会直接影响实现。

例如 undo/redo 题里,只有真正改变状态的操作才应该进入 undo stack。否则用户连续 undo 时会遇到“撤销了一个其实没发生过的操作”,行为会很奇怪。

复杂度优化要围绕增量更新

很多 LLD 面试的 follow-up 都会问性能。

购物车折扣可以每次 get_total_discount 时重新扫描所有 ingredient,但如果查询频繁,就应该在 add/remove 时增量维护 total_discount。

日志统计可以每次查询时排序算 median,但如果数据量大,要考虑每个 endpoint 维护 response time 列表,或者在 streaming 场景里用两个 heap 维护中位数。

权限继承可以每次查询时沿 resource tree 向上找,也可以缓存有效权限,但缓存会带来更新失效问题。

你不一定要一开始写最复杂版本,但要能说明:

先实现正确版本
瓶颈在哪里
如果读多写少怎么优化
如果写多读少怎么优化
缓存失效如何处理

这就是面试官要的工程判断。

状态机比 if-else 更稳

告警引擎、订单、任务调度、游戏状态这类题,最好显式讲状态机。

例如告警可以有:

PASS → CRITICAL:首次通知
CRITICAL → CRITICAL:根据 repeat interval 决定是否重复通知
CRITICAL → PASS:发送 resolve
PASS → PASS:不动作

这样写比一堆 if-else 更清楚,也更容易覆盖测试。

棋类或游戏题也是类似。不要只检查“有没有赢”,还要考虑:

  • 当前轮到谁
  • 落子是否合法
  • 游戏是否已经结束
  • 是否平局
  • 输出格式是否稳定
  • AI 或 random player 是否可能无限循环

很多候选人不是不会算法,而是忘了业务状态的终止条件。

测试用例要覆盖边界,不只是 happy path

LLD 面试里,自己写测试很重要。建议固定准备这几类:

  • 空输入
  • 重复操作
  • 删除不存在对象
  • 边界阈值
  • 状态来回切换
  • tie-breaking
  • 深层嵌套
  • undo/redo 连续调用
  • 新操作清空 redo

以购物车为例,除了“两个 recipe 触发折扣”,还要测:

  • 添加同一个 recipe 两次
  • 删除不存在 recipe
  • 删除后折扣降档
  • undo add
  • undo remove
  • redo 后再新 add,redo 历史被清空

测试越清楚,面试官越容易相信你的代码能上线。

回答模板

遇到 LLD/OOD 编码题,可以按这个顺序:

1. 复述需求和 API 语义
2. 列核心对象和状态
3. 说明关键不变量
4. 先写正确版本
5. 跑样例和边界测试
6. 分析复杂度
7. 讨论 follow-up 优化

关键不变量是很多人忽略的。例如:

  • cart_ 和 ingredient_count_ 必须始终一致。
  • undo_stack 只记录成功改变状态的操作。
  • alert 的 last_notified_at 只在真正通知后更新。
  • resource inheritance 查询不能越过显式 deny。

讲出这些不变量,你的答案会更像工程实现,而不是刷题代码。

结尾

LLD/OOD 面试的难点不是题目有多新,而是它要求你同时写代码、建模、维护状态和解释取舍。准备时不要只背某一道题的解法,要练“拿到一个小系统后如何拆状态、定 API、补测试、做增量优化”。

如果你做算法题还可以,但遇到 OOD、LLD、multi-part coding 容易乱,可以做一次针对性 mock。重点不是给你标准答案,而是训练你在 45 到 60 分钟里把题目推进成一个可运行、可解释、可扩展的小系统。你可以在服务和价格页面查看编码面试和 LLD/OOD 辅导选项;如果已经有具体面试时间,也可以通过联系我们页面说明目标岗位、面试轮次和准备进度。

Previous
Previous

系统设计面试遇到开放题怎么办:Booking、Ticket、Payroll 和 Chat 系统的通用框架

Next
Next

Data Engineer 面试怎么准备:SQL、Python、数据建模和 Pipeline 设计