首页/开发/代码质量与评审速查/防御式编程与快速失败(Fail Fast)
设计原则与模式62 条

防御式编程与快速失败(Fail Fast)

防御式编程检查输入与状态,快速失败在错误发生点立即终止:构建期能拦的不留到运行期,能抛异常的不静默返回;与契约式设计互补——进入系统时严格校验,内部信任契约。

防御式编程与快速失败

防御式编程检查输入与状态,快速失败则在错误发生点立即终止:构建期能拦的不留到运行期,能抛异常的不静默返回。它和契约式设计互补——进入系统时严格校验,内部调用之间信任契约,避免层层重复防御。

基本信息

边界策略外部输入严格校验,失败立即暴露
内部策略信任内部契约,避免重复防御
常用手段前置条件检查、参数断言、显式异常
反模式静默兜底、把异常当控制流
收益错误在源头暴露,栈信息指向真正位置

典型使用场景

  • 处理用户输入与外部接口返回时校验字段
  • 构造函数禁止非法状态进入对象
  • 解析配置时对缺失的必填项直接失败
  • 状态机里拒绝非法状态转移
  • 内部纯函数之间不做重复判空

失败方式对照

失败方式对照
// 静默兜底:错误数据进入下游,问题延后暴露
if (qty == null) qty = 1;

// 快速失败:就地拦截,栈信息指向源头
Objects.requireNonNull(qty, "qty required");
if (qty <= 0) throw new IllegalArgumentException("qty must be positive");

在哪一层校验

位置做法说明
边界入口严格校验,快速失败外部数据不可信
领域内部校验不变式与状态保证对象始终合法
内部调用之间信任契约,不重复校验减少臃肿与重复代码
快速失败的价值在定位:错误在离源头最近处抛出,日志与堆栈直接指向问题;静默兜底会把问题推到几层之后,排查成本成倍上升。
不要用异常处理可预期的分支:这类控制流会让代码难读、性能也受影响;可预期的情况用返回值或结果对象表达更清楚。
校验边界通常只有几处:外部接口、用户输入、配置文件与消息体。把这些入口做实,内部代码就能按契约书写,重复判空会明显减少。

易错点

  • 内部调用层层重复校验:代码臃肿、性能受损,真正需要校验的边界反而不突出
  • 把异常用作控制流:可读性与性能都被拖累
  • 静默兜底隐藏错误:非法数据进入下游,问题延后暴露且难以溯源
  • 只校验外部输入不校验内部状态:对象可以进入非法状态,后续调用到处异常

数据来源与参考

  • 《The Pragmatic Programmer》防御式编程与 Design by Contract 实践
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌