代码里的岔路口:我如何看待编程中的条件判断艺术
关于 if else 是不是好代码,这个问题的争论几乎和编程本身的历史一样长。
我最早接触这个矛盾是刚入行那年。有一次 code review,我看见前同事写了一个三十多行的 if else 链,最离谱的是嵌套六层,每层还有不同的 return 路径。我当时觉得“这代码能跑,但看着真难受”,但你要问我哪里不好,我那时候也答不上来。后来几年做了不少项目,从嵌入式 C 到 Web 后端再到数据管道,也跟很多团队合作过,慢慢发现自己对 if else 的看法成熟了很多。结论是:if else 本身没有原罪,它是最基础的分支表达方式。它之所以被频繁批判、被各路“最佳实践”建议用设计模式替代,真正的问题在于对人脑认知负担的透支。
这篇文章想聊聊我对条件判断这件事的完整思考:if else 为什么不会消失、什么时候它会变成代码坏味道、以及我们应当用什么标准去衡量一段带 if else 的代码到底好不好。里面会穿插一些具体的例子、踩坑记录和工具选择思路,希望对正在纠结这段逻辑怎么写的朋友有一点参考价值。
1. 条件判断的本质:为什么我们的代码绕不开 if else
1.1 从 CPU 的分支指令说起
先看一个事实:现代 CPU 里,分支指令是最常见、也是影响性能最显著的指令之一。你在高级语言里写一个 if,编译器最终会把它变成类似 cmp、jmp、jne 这样的指令组合。CPU 为了不让流水线停下来,还专门设计了分支预测器,依据历史执行记录猜测下一条分支往哪走。
所以我们平时写下的 if,在硬件层面代表的是“程序执行流的岔路”。只要这个世界存在“不同输入需要不同处理方式”的需求,分支就不可能被完全消灭。你写 SQL 用 CASE WHEN,写 Python 用 if/elif/else,写 Go 用 switch,写 PLC 用触点和线圈组合实现条件跳转,本质上都是同一件事:对状态进行判断,再决定走向。
想通了这一点,就不会再纠结“能不能完全不用 if else”。不能,也没必要。更值得关心的是:我用什么粒度、什么方式来组织这些分支,才能让代码在三个月、半年后还能被迅速理解。
1.2 编程范式演进中的分支策略变迁
很多人对 if else 的反感,其实来源于对它被滥用的反感。去看看编程范式的发展路径就知道了:面向过程时代,我们习惯用大量 if 判断当前模式,再调用不同函数;走到面向对象时代,多态成为替代 if 的经典方案;再后来,函数式编程用 Option、Either、模式匹配把分支变成表达式的一等公民;而现在很多新语言干脆把分支判断融合在编译期类型系统里。
Go 就没有三元运算符,官方态度很明确:你老老实实写 if 分支,别整花活。Rust 则是另一条路线,match 表达式配合模式匹配能在一段代码里优雅地处理各种复合条件。Java 从 14 年开始正式支持 switch 箭头语法和模式匹配,本质上也是为了降低分支代码的心智负担。
这些演进说明了一个趋势:开发者们从未停止寻找 if else 的替代品,但替代它的往往不是“没有分支”,而是“更结构化的分支表达”。我们对 if else 的厌恶,更多是在厌恶“失控的分支”,而不是“分支”本身。
1.3 一个过拟合神话:不要神话多态,也不要神话表驱动
网上讨论这个问题时,常常有一种声音:只要看见 if else 就让你改成策略模式,或者让你用表驱动。我自己早期也犯过类似的错误。
有一次写一个文件解析器,里面要根据文件扩展名选择不同的解析逻辑。我按“最佳实践”设计了一堆接口、实现类、工厂,写完以后洋洋得意。结果后来需求变化,扩展名从三个涨到十几个,每个解析器之间有细微的共享逻辑差异,还牵扯到不同版本之间的兼容策略。那套多态的抽象最后变成了一个巨大的迷宫:你为了知道“这个 docx 走的是哪个解析路径”,要把工厂、注册表、抽象类、泛型参数来回翻好几遍。相反,项目里一个不太起眼的 if ext == "docx" { ... } 反而好读得很。
后面我学乖了。工具要服务于代码的核心指标(可理解、可修改、易测试),而不是为了证明自己在用设计模式。实践下来,最合理的策略往往是:分支少(三五个以内)时直接 if,分支多但条件彼此独立时考虑表驱动,分支多且伴随每个分支各自的行为逻辑时再考虑多态或策略模式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一段 if else 代码为什么会“烂”
2.1 复杂度指标:圈复杂度如何暴露问题
在软件工程里,圈复杂度是一个直接用来衡量条件逻辑复杂度的指标。它的计算方式很简单:把代码看成是图结构,每出现一个 if、for、while、case,就表明多了一条可走的路径。圈复杂度越高,说明一个函数里可能出现的路径数量越多,你要在每个路径上都测一遍的成本就越大。
一般来说:
- 圈复杂度在 1~10 之间,代码还算易于维护;
- 11~20 之间,开始有理解难度,需要仔细设计测试;
- 21~50 之间,基本属于高危区,很容易改一处炸三处;
- 超过 50,我强烈建议直接重构。
很多团队把圈复杂度作为 CI 的检查项,超阈值就挂红灯。我觉得这是个好习惯。但要注意一点:圈复杂度只是“症状指标”,它告诉你代码变复杂了,却不会告诉你“能不能用别的方式表达”。所以正确用法是:当某个函数圈复杂度飙高时,先不要急着写更多 if,停下来看一眼这段逻辑的内在结构,是不是该分层、拆分、或者换表达方式了。
2.2 三种典型的 if else 滥用场景
实际代码里,烂 if else 通常长这样:
第一种是嵌套过深。比如:
c复制if (user != NULL) {
if (user->isActive) {
if (order != NULL) {
if (order->paid) {
// do something
}
}
}
}
这种写法最可怕的地方在于:你每增加一层缩进,大脑就需要多记住一个隐含前提。几层之后,人的工作记忆就彻底爆炸了。阅读者想回答“什么情况下会走到最后的逻辑”这个问题,必须把所有外层条件全部叠加一遍。
第二种是重复条件散落各处。同样的 if (user != NULL && user->isActive) 出现在十个不同函数里,一旦需求改成“已注销用户也可以部分访问”,你就得满文件搜。
第三种是硬编码分类导致脆弱的扩展性。比如:
java复制if (orderType == 1) { ... }
else if (orderType == 2) { ... }
else if (orderType == 3) { ... }
没有任何注释说明 1、2、3 分别代表什么。新同事接手直接懵了,后来人为了兼容某个特殊需求,又开始叠 if,很快整个模块就变成一团乱麻。
2.3 可读性 vs 性能:常见的误区权衡
有一种声音会替 if else 辩护:写着简单,性能好。这个论点部分成立,但也只对极少数性能敏感的基础库适用。
拿分支预测来说,如果某个分支在绝大多数情况下都走同一路径,CPU 预测会非常准;但如果你在一个循环里跑随机数据,每次分支结果都不同,预测失败带来的流水线惩罚是真实可感的。这就是为什么在一些高性能数值计算场景下,开发者会故意把数据重排,让同一 warp 或 vector 里的元素尽量走向同一分支。
但这些都是底层优化专家的领域。绝大多数业务代码根本不是性能瓶颈,即便一个 if else 链多消耗几个纳秒,对用户毫无感知。反而是一次逻辑转弯造成的理解成本,会让团队多花三个小时去追问这段代码到底想干嘛。我们太容易高估“机器执行时间”的重要性,又太容易低估“团队阅读代码的时间”的代价。在业务代码里,可读性和可维护性几乎总是优先于微性能。
3. 我实际用过的 if else 替代方案
3.1 卫语句:把早返回当做第一层梳理
卫语句是成本最低、效果最明显的替代手段。它的核心思想是:把不符合条件的情况一条条地“先赶走”,剩下的就是核心逻辑。
c复制if (user == NULL) {
return;
}
if (!user->isActive) {
return;
}
if (order == NULL) {
return;
}
if (!order->paid) {
return;
}
// do something
和前面嵌套六层的版本相比,卫语句的结果是函数的中后段变得非常平缓,读代码的人不需要记住前面的每个 if 条件,因为在执行到核心逻辑时,前面所有不满足条件的情况都已经返回了。这也是我在 code review 中提得最多的建议之一,凡是看到函数入口处连续几层嵌套校验,我会直接回复一句:“考虑用卫语句先把这些条件全挡掉。”
卫语句不是万能的。有时候分支之间的“主逻辑”并不是唯一的,比如状态机里,每个状态都有自己对应的处理逻辑,这时候你用卫语句就会变成一堆 return 加堆尾处理,反而没意义。卫语句最适合的场景是“前置校验 + 单一主逻辑”。
3.2 表驱动编程:让数据替代代码
如果说卫语句是梳理形状,那表驱动就是彻底换一种写法。其核心思想是:把条件判断转化为查表。
我当年刚学 PLC 编程的时候,老师讲的第一个高级技巧就是用“功能表”代替“梯形图里的一堆触点条件”:把输入组合和输出结果放进表格里,程序逻辑只需要去查找表。后来在嵌入式 C 里写命令解析器时,我发现这招同样好用。传统写法是:
c复制if (cmd == CMD_START) {
start_machine();
} else if (cmd == CMD_STOP) {
stop_machine();
} else if (cmd == CMD_RESET) {
reset_machine();
}
改成表驱动以后:
c复制struct command_entry {
int cmd;
void (*handler)(void);
};
static struct command_entry cmd_table[] = {
{ CMD_START, start_machine },
{ CMD_STOP, stop_machine },
{ CMD_RESET, reset_machine },
};
// decode
for (int i = 0; i < ARRAY_SIZE(cmd_table); i++) {
if (cmd_table[i].cmd == cmd) {
cmd_table[i].handler();
break;
}
}
注意,表驱动的优点不在于“省了几个 if”,而在于:命令和处理函数的对应关系从程序逻辑里被剥离出来了,新增一个命令只需要往表里加一行,不需要修改任何控制流。这在命令种类特别多(比如几十种)的场景下效果明显,相当于把硬编码的分类规则变成了数据配置。
别以为只有 C 语言能用表驱动。Python 里用 dict 映射函数,Java 里用 EnumMap,Go 里用 map[string]func(),都是非常自然的表驱动形态。我甚至写过一版用 YAML 描述扩展名和解析器对应关系的配置,让非开发人员也能维护映射。
3.3 多态与模式匹配:职责分配的正确姿势
当各个分支不仅返回值不同,而且后续逻辑差异非常大、相互之间几乎没有共用代码的时候,多态是更合适的选择。本质上,多态是把“根据类型选择不同行为”这一分支判断交给了运行时/编译期,让调用方只依赖接口,不感知具体实现。
假设要有不同的折扣策略,用 if 写:
java复制double calculateDiscount(String userLevel, double amount) {
if (userLevel.equals("normal")) {
return amount * 0.05;
} else if (userLevel.equals("vip")) {
return amount * 0.1;
} else if (userLevel.equals("svip")) {
return amount * 0.2;
}
return 0;
}
用策略 + 枚举实现:
java复制public enum UserLevel {
NORMAL {
@Override public double discountRate() { return 0.05; }
},
VIP {
@Override public double discountRate() { return 0.10; }
},
SVIP {
@Override public double discountRate() { return 0.20; }
};
public abstract double discountRate();
}
调用方只需要简单一行:amount * userLevel.discountRate()。
很多讲“用多态替代 if else”的例子只讲到这里,但实际工作中我感受最深的其实是另一个点:多态真正解决的是“未来扩展方向”的问题。如果你判断依据本质上是一个会频繁新增的类型,并且每个类型的处理逻辑都相对独立,那么多态会让新增类型变得非常安全:你只需要新增一个实现类,不需要改动已有代码。这正好符合开闭原则。
如果语言本身支持模式匹配,比如 Rust 的 match 或 Kotlin 的 when,那条件逻辑的写法可以同时获得可读性和安全性的提升。Rust 编译器会强制你处理所有枚举可能的分支,这等于把漏判某种情况的 bug 直接在编译期拦住了,这是普通 if else 做不到的。
3.4 状态机:从“一堆 if”到“一张转移表”
很多复杂业务场景,比如订单状态流转、协议解析、游戏角色行为控制,本质上是一个有限状态机。如果一个状态能转移到很多状态,你再用 if else 去写,基本就是灾难:每一处转移都要判断当前状态是什么,然后判断事件是什么,再决定目标和副作用。
我见过用最笨的方式写的订单状态逻辑,大概长这样:
c复制if (orderState == CREATED && event == PAY) {
orderState = PAID;
sendNotification();
} else if (orderState == CREATED && event == CANCEL) {
orderState = CANCELLED;
} else if (orderState == PAID && event == SHIP) {
orderState = SHIPPED;
callLogisticsApi();
}
// ...
这个函数后面扩展到二十多个分支时,已经没人敢改了。后来我们干脆画出一张状态转移图:状态是节点,事件是边,转移动作挂在边上。然后把这张图翻译成一张转移表,代码变成:
c复制struct transition {
int from_state;
int event;
int to_state;
void (*action)(void);
};
struct transition transitions[] = {
{ CREATED, PAY, PAID, sendNotification },
{ CREATED, CANCEL, CANCELLED, NULL },
{ PAID, SHIP, SHIPPED, callLogisticsApi },
// ...
};
如果你使用支持枚举和类型系统的语言,状态机还可以做得更安全。比如 Rust 的 typestate 模式,直接在类型层面把“哪些状态允许哪些转移”编译期固定住。C++ 也可以用模板元编程做类似的事情,但业务代码里通常不需要那么复杂,一张转移表已经足够了。
状态机方案不只是在节省代码量,它最大的价值是让你把思维从“写 if”变成“定义状态转移关系”。代码的可维护性和可验证性来自这个思维转换,而不只是写法。
4. 特殊领域里的条件判断规则
4.1 安全关键系统:MISRA C 的坚持与反思
聊到 if else 的规范,就绕不过 MISRA C。这是汽车嵌入式行业广泛采用的一套 C 语言编码标准。
MISRA C 里有几条规则至今仍是程序员社区争论的热点:
- Rule 15.6:
if语句体必须使用花括号。 - Rule 15.7:
if ... else if的最后一个else if必须有一个对应的else分支。 - Rule 15.5:函数末尾不允许有不可达的
else if。
很多没在安全关键行业待过的朋友看到第二条(if 必须以 else 结尾)会觉得不可思议:明明很多分支不需要 else,不写 else 代码才简洁啊。
我理解这种疑惑,但在汽车电子或医疗设备这些行业里,规则背后的理由是明确的:当系统执行到一个分支时,必须显式说明“其他所有未列出情况将会怎样”。如果没写 else,就相当于漏掉了一种潜在情况,而那种情况在安全关键场景里可能就是车祸或者生命危险。MISRA 宁愿让你写空 else 并在注释里说明“这里明确什么都不做”,也不要暗示编译器“其他情况不可能”。
当然,我并不是建议所有行业都把 MISRA 规则照搬过来。大部分互联网业务场景里,过度追求“每个 if 都要 else”反而会制造大量无效代码。但这里有一个值得借鉴的底层原则:在分支里,明确“被遗漏的情况会怎样”有时候比代码简洁更重要。
4.2 异步编程中的条件分支:从 CompletableFuture 到协程
异步编程给条件判断增加了一个新的复杂度维度:你不仅要判断“走哪个分支”,还要判断“分支是同步返回还是异步回调”。
Java 里用 CompletableFuture 写条件逻辑时就经常踩坑。比如有段代码是“如果缓存命中就返回缓存,否则去数据库查”,一不小心就会写成:
java复制return cache.get(key)
.map(CompletableFuture::completedFuture)
.orElseGet(() -> CompletableFuture.supplyAsync(() -> db.query(key)));
这段代码还算简单,但一旦牵扯到多个异步阶段,每个阶段都要做条件判断,还要处理异常分支,代码就会迅速膨胀。CompletableFuture 的异常分支本质上也是一种条件判断:exceptionally、handle、whenComplete 各有不同的语义,选错了就会出现“异常吞掉”或者“重复处理”的 bug。
Python 的 asyncio 相对好一点,因为协程是顺序代码,条件判断和普通代码一样写,不需要嵌套回调。这其实侧面说明了一个更通用的原则:条件判断在“顺序执行”的模型下最容易理解。所以当你发现代码里全是异步回调和分支的纠缠时,与其想着怎么优化 if 的姿势,不如先考虑换成协程、或者用更扁平的数据流结构来消除异步分支嵌套。
4.3 PLC 与嵌入式环境:可视化世界的条件判断
PLC 编程和传统高级语言不太一样。梯形图里的触点串联就是 if AND,触点并联就是 if OR,输出线圈就是赋值。你在 PLC 里写条件判断时,图形本身就承担了可读性的一部分职责。
但正因为是图形化,分支多了以后梯形图会连成一张密不透风的蜘蛛网。我在调试过一段别人写的 PLC 程序后有种强烈的感受:梯形图里的逻辑回路和高级语言里的 if else 嵌套其实共享同一个复杂度问题,最好的解法也很类似,就是把重复出现的组合条件抽取成中间变量(内部继电器),让图形逻辑扁平化。
在嵌入式 C 领域还有另一件常见问题:宏定义和条件编译。#ifdef 大量使用会让代码在不同的编译配置下呈现完全不同的可读性,很多时候你看到的 if 不是运行时的分支,而是编译期的分支。这类代码更难排查,因为它根本不会出现在所有编译环境下。我的建议是:#ifdef 的使用必须克制,最好限制在“平台差异适配”这一层,不要用来做业务逻辑的开关。
4.4 数据驱动的启发式判断:AI 时代的分支新形态
最近这两年风很大的 AI 编程助手,其实也在不断改变我们写分支逻辑的方式。用提示词让 AI 帮你生成代码时,你给出的条件描述越精确,它生成的分支就越规范。反过来,AI 生成代码最常见的错误之一,就是漏掉边界情况分支,或者对空值判断不完整。
所以我现在写提示词时会刻意加一句“考虑所有 null/空值/异常情况,给出完整的卫语句”,这几乎是一种必备的提示技巧。AI 时代的条件判断,一部分变成了你描述需求时对隐含分支的描述能力。
另外,如果你想用传统规则引擎的方式处理复杂的条件判断,可以关注一下决策树和规则引擎。有一些项目把复杂的业务判断规则外置到 DSL 或图表中,运行时再翻译成执行逻辑。这种思路在金融风控、营销活动配置这些“规则经常变又不能发版”的场景里非常有用。本质上,这也是一种“把代码里的 if 变成数据”的表驱动思想。
5. 如何判断自己的 if else 是好是坏
5.1 好味道与坏味道速查清单
我在实际写代码和做 review 时,逐渐总结了一套判断分支代码健康度的速查清单,分享在这里:
值得放心的迹象:
- 函数入口处先有一组干净的卫语句,校验完立刻返回;
- 每个 if 分支体很短,能看到“做了什么”和“为什么返回”;
- 分支条件是可读的具名函数或常量,不是一大串表达式;
- 增加一种新类型/新命令时,不需要改动已经写完的分支代码;
- 单测能覆盖每个分支的返回值,缺一个 case 会直接体现为测试失败。
需要警惕的迹象:
- 嵌套超过三层,或者缩进层次比函数实际逻辑还长;
- 同一组条件(尤其是
!= NULL之类的判空)在多个函数里反复出现; - 分支里的代码会在不同条件下产生大量重复副作用;
- 条件本身就具备“一层套一层”的语义,比如状态、事件都在多个维度变化;
- 改一个分支会影响另一个看似无关的分支的返回结果。
5.2 方法一:用代码审查锻炼条件逻辑的敏感度
锤炼判断力最好的场景就是 code review。
我现在的习惯是,review 时先看函数的圈复杂度和函数长度。一旦某个函数超过三十行,或者嵌套层级超过三层,我会认真问三个问题:
- 这个函数到底在表达几件不同的事?
- 哪些条件可以合并、那些可以提前返回?
- 有没有一张表/一张图能比这一堆 if 更清楚地表达同样的事实?
这三个问题不一定每次都有答案,但它们逼着阅读者去抓住分支逻辑的深层结构,而不是停留在“这代码能跑就行”的层面。时间久了,你会迅速对一些“坏味道”产生直觉反应,就像经验丰富的厨师看一眼菜色就知道火候不对。
5.3 方法二:从测试角度看可测性
一个非常实用的判断标准是测试覆盖率。如果你的函数有条件分支但没有任何测试走到另一个分支,那这段 if else 大概率是隐含了未验证的风险。
我曾经接手过一个遗留系统,里面有个函数处理三种支付渠道的对账逻辑,if else 密密麻麻写了一百五十行。我一开始想硬着头皮直接加功能,后来决定先把每个分支的输入条件梳理出来,写成一张参数表,再逐个分支写单元测试。这个准备工作做完,我惊讶地发现这个函数根本不只是一百五十行代码:它实际上包含了七种不同的“渠道状态”组合,其中三种组合被业务方明确不打算支持但也没有禁用。也就是说,那段代码在默默执行着“不支持”逻辑,却没人知道。
之后我把这个函数重构为查找表:渠道类型和状态作为两个维度,解析结果作为表格里的单元格。分支不再是一连串 if,而是一次索引查表。代码短了一半,但更关键的是:任何渠道状态组合的数据都能直接查表获得预期结果,测试从“每个 if 都要构造一次”变成了“枚举表格里的所有单元格”,遗漏风险大幅下降。
这个经历让我深刻意识到,分支复杂度高的时候,问题的核心不是“怎么用更酷的方式写 if”,而是“怎么让所有逻辑组合可见、可枚举、可测试”。
5.4 方法三:代码提交前做个自我重构
推荐每个人在提交代码前,做一次快速自我重构,重点就放在条件逻辑上。
我一般会做这几件事:
- 把所有嵌套 if 改成卫语句,除非这里确实是一棵对称的决策树;
- 把每个条件子句里超过两个布尔运算的组合抽成带名字的函数;
- 如果同一函数的多个分支里出现相同的子逻辑,立刻提取成局部函数;
- 尝试用表驱动替代最内层的选择逻辑;
- 运行一遍单测,确保重构没有改变任何行为。
这一套流程下来大概花十几分钟,但收获非常大。它能让你在别人看到代码之前,先用自己的标准过滤一遍。三个月的代码维护期里,这十几分钟的收益会被反复放大。
6. 从实战中积累的几个小技巧
6.1 用枚举替代魔法数字,别让 if 后面跟着裸 1
一个常见错误是条件里直接出现魔法数字:if (process == 1)。问题有两个:一是读代码的人不知道 1 是什么,二是将来新增一个 2 或 3 时,可能没有任何检查点能帮你发现疏漏。
之前做 PLC 和嵌入式结合的项目时,我们定了条铁律:所有发给控制器的指令码,必须在头文件里定义成枚举或宏。指令码的类型判断全用符号名,编译期就能抓住拼写错误。这个习惯延续到我后面的所有代码里,基本上杜绝了“1 到底是启动还是暂停”这类只有在生产环境里才会暴露的问题。
6.2 分支代码块要短,业务逻辑要被“看见”
if 后面跟着一大段五十行的逻辑,这段代码就已经超出了单个分支的控制范围。读代码的人看不到分支本身,满眼都是分支后的具体实现。
所以我会尽量让分支体保持短小,分支里的逻辑要么提取成函数,要么转成表驱动中的 action。这样每个分支看起来就是一行调用,真正的主流程可以沿着调用列表快速读下去。
写函数式风格的代码时也是一样的:你可能在一段 Lambda/Stream 管道里写一个 filter,但如果 filter 里的判断条件超过一行,我建议直接抽成一个函数,取一个清晰的名字。
6.3 给未来的维护者写“分支说明”
最后一个看似不那么技术性的经验是:给复杂的分支条件写注释。
什么是复杂分支条件?比如“如果用户是会员且是 30 天内的新注册用户且来自 A 渠道”这种三个条件 AND 在一起的判断。在代码里直接写:
python复制if user.is_vip and user.created_at > 30_days_ago and user.channel == "A":
从可读性上勉强可行,但为什么需要这三个条件同时满足呢?这时候一段注释比任何重构技巧都更能帮到未来的维护者。我通常会在这种条件上面写一行注释,说明“这个分支处理的是 5 月活动期间的新渠道会员的优惠回收逻辑,之所以排除老会员是因为他们的回收率没有统计意义”。
这种注释没增加任何逻辑,但在半年后有人想改条件时,他可以知道自己要改的是活动策略,而不是随手删掉其中一个条件。写条件判断时有个朴素的思考:当这个代码需要被修改时,修改它的人能不能从代码本身理解“为什么这里有这个条件”?如果能,你的 if else 就是好代码。
我自己在长期实践中最大的体会有两点。第一,不要迷信某种写法是“银弹”。卫语句、表驱动、多态、状态机、模式匹配,每一种都有适合的场景。第二,判断 if else 好不好,最重要的标尺是“这段代码需要多久才能被别人看懂和修改”,以及“修改错误的风险有多高”。很多程序员喜欢追求精简的代码或者设计精巧的抽象,却忽略了代码的本质是用来交流的。一段漂亮的 if else,应该像一条清晰的岔路标牌:告诉每个路过的人,每条路通向哪里,为什么这样走。做到这一点,逻辑再多的条件判断也是好代码。
