卫语句应“先拦后干”,每条只做一件事并立即退出:先参数校验(如空值)、再上下文校验(如登录态)、最后业务校验(如订单状态),主流程无缩进;条件需语义化(如isPaymentConfirmed而非status==2);多正常分支或需统一兜底时不宜使用。

用卫语句重构深嵌套 if-else,核心是“先拦后干”:把所有拦截性判断(空值、状态非法、权限不足等)提前写成独立的 if + return 或抛异常,主业务逻辑自然浮到最外层,缩进归零。
卫语句怎么写才对
每条卫语句只做一件事,且必须带明确退出动作。不是简单加个 if,而是“条件成立 → 立即终止”。
- 校验空值:
if (user == null) throw new IllegalArgumentException("用户不能为空"); - 检查状态:
if (!order.isDraft()) return;(非草稿态直接不处理) - 权限控制:
if (!hasPermission("DELETE")) return Result.forbidden(); - 避免拼凑:
if (user == null || !user.isActive() || user.isLocked())这种写法违背卫语句原则——错误定位模糊,日志难追踪,应拆成三条独立判断
重构时的关键顺序
按执行依赖从上到下排列卫语句:越前置的检查,越影响后续逻辑是否该运行。比如先判对象是否存在,再判其状态,最后判业务规则。
- 第一步:参数级校验(null、基本格式)
- 第二步:上下文级校验(用户登录态、租户有效性)
- 第三步:业务级校验(订单是否可编辑、库存是否充足)
- 主流程代码放在所有卫语句之后,无任何缩进,一眼可见
配合语义化命名提升可读性
卫语句的条件表达式要像一句自然语言断言,而不是技术判断。
立即学习“Java免费学习笔记(深入)”;
- 别写
if (status == 2),改写为if (!isPaymentConfirmed(order)) - 别写
if (list.size() == 0),改写为if (items.isEmpty()) - 方法名或布尔变量名本身就能说明意图,比如
shouldSkipValidation()比skipFlag更可靠
什么时候不该用卫语句
卫语句适用于“守门”场景,不是万能解药。
- 多个分支都属于正常业务路径(比如支付方式选择:微信/支付宝/银行卡),不适合用卫语句,该用策略模式或 Map 映射
- 需要统一兜底处理的场景(如所有异常都记录审计日志再抛出),卫语句提前返回会绕过统一处理,此时应保留一层 try-catch 或使用 AOP
- 条件之间有强依赖且必须串行计算(如风控评分链),强行拆成卫语句反而割裂逻辑流


















