13. 简化条件逻辑(一):分解条件表达式、合并条件表达式、以卫语句取代嵌套条件表达式
条件逻辑,说白了就是代码里的「岔路口」。
我见过太多项目,代码读起来像迷宫——一个 if 套一个 if,再套一个 else if,最后连写代码的人自己都绕晕了。今天咱们聊聊三个最常用的简化手法:分解条件表达式、合并条件表达式、以卫语句取代嵌套条件表达式。
核心原则:条件逻辑应该像高速公路,而不是乡间小道。每个分支都清晰可见,没有隐藏的弯道。
一、分解条件表达式:把大块头拆成小零件
先看一段代码。你感受一下:
// 重构前
if (date.after(plan.summerStart) && date.before(plan.summerEnd)) {
charge = quantity * plan.summerRate;
} else {
charge = quantity * plan.regularRate + plan.regularServiceCharge;
}
这段代码其实不难,但每次读都要在脑子里过一遍「夏天是什么时候?夏天费率是多少?冬天又是什么规则?」。说白了,条件本身和分支逻辑混在一起了。
我个人习惯的做法是:把条件判断抽成函数,把每个分支的计算也抽成函数。
// 重构后
if (isSummer(date, plan)) {
charge = summerCharge(quantity, plan);
} else {
charge = regularCharge(quantity, plan);
}
function isSummer(date, plan) {
return date.after(plan.summerStart) && date.before(plan.summerEnd);
}
function summerCharge(quantity, plan) {
return quantity * plan.summerRate;
}
function regularCharge(quantity, plan) {
return quantity * plan.regularRate + plan.regularServiceCharge;
}
我的经验:我在重构一个支付系统时遇到过类似情况。原来的条件判断写了整整 30 行,里面还嵌套了三个 if。拆成 5 个小函数后,代码行数没少多少,但可读性翻了三倍。后来新同事接手,看一遍就懂了。
你想想看,拆完之后主流程变成了什么?就是一句「如果是夏天,算夏天费用;否则算普通费用」。这不就是业务人员嘴里说出来的话吗?
二、合并条件表达式:别写重复的「如果」
有时候我们会遇到这种情况:多个不同的条件,最终执行的是同一段逻辑。
// 重构前
if (age < 18) {
return "未成年人禁止购买";
}
if (isBlacklisted(user)) {
return "未成年人禁止购买";
}
if (orderAmount > 10000 && !user.isVerified) {
return "未成年人禁止购买";
}
嗯,这里要注意——三个条件都返回同一个字符串。但代码却写了三遍。读代码的人会想:「这三个条件有什么不同?为什么分开写?」
合并之后,逻辑就清晰了:
// 重构后
if (isUnderage(age) || isBlacklisted(user) || isHighRiskOrder(user, orderAmount)) {
return "未成年人禁止购买";
}
function isUnderage(age) {
return age < 18;
}
function isBlacklisted(user) {
return user.status === 'BLACKLISTED';
}
function isHighRiskOrder(user, orderAmount) {
return orderAmount > 10000 && !user.isVerified;
}
避坑指南:我曾经犯过一个错误——把条件合并得太狠。比如把「用户是VIP」和「订单金额大于1000」合并成一个条件,但后来业务改了,VIP用户要单独处理。结果又得拆开。所以合并前先想清楚:这些条件在业务上是不是真的「同一种情况」?
合并条件表达式的核心原则就一条:结果相同,条件合并;结果不同,条件分开。
三、以卫语句取代嵌套条件表达式:提前说「不」
这是我最喜欢的手法。很多新手写代码喜欢这样:
// 重构前
function getPayAmount(employee) {
let result;
if (employee.isRetired) {
result = 0;
} else {
if (employee.years > 10) {
result = employee.salary * 1.5;
} else {
result = employee.salary * 1.2;
}
}
return result;
}
你看,这里有一个 if-else 套着另一个 if-else。读代码的时候,你的脑子要同时记住外层条件和内层条件。累不累?
用卫语句改写:
// 重构后
function getPayAmount(employee) {
if (employee.isRetired) {
return 0; // 卫语句:特殊情况提前返回
}
if (employee.years > 10) {
return employee.salary * 1.5;
}
return employee.salary * 1.2;
}
差别在哪?卫语句把「特殊情况」提前处理掉,剩下的就是正常流程。你想想看,读代码的时候,看到 if (isRetired) return 0,你马上就知道「哦,退休的人直接拿0,不用往下看了」。
什么时候用卫语句?一个简单的判断标准:如果某个条件分支是「异常情况」或「边界情况」,就用卫语句提前返回。如果所有分支都是「正常流程」,那用 if-else 也没问题。
四、三种手法的对比与选择
| 手法 | 适用场景 | 核心收益 | 注意事项 |
|---|---|---|---|
| 分解条件表达式 | 条件判断复杂,或分支逻辑较长 | 提升可读性,让条件「自解释」 | 不要过度拆分,每个函数只做一件事 |
| 合并条件表达式 | 多个条件执行相同逻辑 | 消除重复,减少分支数量 | 确认业务上确实是「同一类情况」 |
| 卫语句取代嵌套 | 嵌套超过两层,有明确的「特殊情况」 | 减少嵌套深度,突出正常流程 | 不要把所有条件都写成卫语句 |
这三种手法不是互斥的。我在实际项目中经常组合使用:先用卫语句把异常情况提前返回,再把剩下的条件拆成小函数,最后看看有没有可以合并的条件。
一个小技巧:重构条件逻辑时,先画一个「条件分支图」。把每个条件、每个分支的结果画出来。这样你一眼就能看出哪些可以合并、哪些可以提前返回。我每次重构复杂条件逻辑前都会画这个图,比直接改代码靠谱多了。
最后说一句:条件逻辑重构的目标不是「消灭 if」,而是「让 if 变得好读」。好的条件逻辑,读起来就像在讲故事——先处理异常,再处理正常情况,每个分支都清晰明了。
公众号:蓝海资料掘金营,微信deep3321