4.3 烂代码传奇
从凭审美给代码贴好坏标签走到以行为证据裁决结构改进,沿重构气味、测试保护和接口比较诊断烂代码。
学习目标
- 能沿锁定行为、识别气味、小步改动、运行测试和比较接口追踪一次安全重构
- 能解释重复、过长职责、隐式依赖和高耦合如何成为可验证的结构信号,而不是审美标签
- 能在正常、边界和故障场景中回答:重构同时改变需求时,哪一条行为证据让失败无法归因
为什么需要这一机制
本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 4.3 烂代码传奇。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。
4.3 烂代码传奇 不能停在“代码看起来丑”。真实系统要解决的变化是从 凭审美给代码贴好坏标签 到 以行为证据裁决结构改进;可执行机制是:重复、过长职责、隐式依赖和高耦合是可操作信号,重构应在测试保护下小步保持外部行为。
三个会让重构失真的陷阱
一个目录节点到重构证据
4.3 烂代码传奇
在 ↡用外部行为、结构信号和测试证据判断代码是否需要改进,并以小步保持合同的工程实践。 中,重构合同可以写成
这里的行为包括成功结果、错误、边界、顺序、副作用和可观察接口;只有结构变化而合同保持,才是安全重构。需求改变应另开变更,不能借重构掩盖语义差异。
锁定行为
↡在改动前记录输入、输出、异常、时序和副作用,形成可以反复运行的外部行为基线。是第一道保护。基线不只是一个示例,它还要包含边界和失败样本,并说明哪些输出是合同的一部分。
识别气味
↡把重复、过长职责、隐式依赖和高耦合等结构信号转成具体可定位的改进假设。不等于宣布代码有罪。每个气味都应指向一个风险、一个范围和一个可验证的小步,而不是泛泛重写。
小步改动
↡一次只改变一个结构原因,保持外部接口和行为不变,并让差异可审查、可回退的重构方式。让失败可归因。提取函数、改名、拆职责或隔离依赖后,立即运行基线测试,不要累积一大批未经验证的变化。
运行测试
↡在每个重构小步后运行回归、边界和异常测试,记录环境、结果和首个失败的验证阶段。是行为保护,而不是发布口号。测试失败时先停止并定位,不要继续堆叠修改让证据变得模糊。
比较接口
↡比较改动前后的成功、错误、边界、时序和副作用,确认外部合同仍然成立的阶段。把“看起来更好”变成可判定结论。接口比较也要覆盖异常类型、日志、事件和资源责任等调用方可见信号。
最小可重放实现
baseline = captureBehavior(clean_version, cases)
candidate = refactor_one_smell(clean_version, one_change)
assert compareBehavior(baseline, run(candidate, cases)).equal
assert reset_and_run(cases) == baseline这段实现草图只表达重构合同,不复制原书叙事或代码。实际运行时应保存样本、接口、异常、结构信号、差异、测试报告和复位结果,使另一位读者能够从干净状态重放。
五步复核一次重构
1. 锁定成功、边界与异常行为
记录输入、输出、错误、顺序和副作用。先预测基线案例的完整结果,再运行并保存快照;没有基线就不能声称行为保持。
Lab
重构行为保持与归因实验
先预测小步改动会影响哪一条行为,再切换边界覆盖与需求混入并比较基线。
提取一个职责后,成功、异常和边界接口均与基线一致
baseline=cases-a → smell=long-method → one-step=extract → tests=pass → compare=equal
判定
accept:结构风险下降且行为保持
当前样本:行为保持;保存行为快照、气味位置、差异、测试报告、首错、接口比较和复位轨迹。
正常、边界与故障证据矩阵
| 样本 | 只改变的变量 | 预期判定 | 必存证据 |
|---|---|---|---|
| 正常 | 一个结构气味,一个小步,需求不变 | 外部行为保持,结构风险下降 | 基线、差异、测试、接口比较 |
| 边界 | 输入范围、异常或时序样本 | 行为合同保持或明确记录差异 | 案例、错误、时序、副作用 |
| 故障 | 大量重写并混入需求变化 | 拒绝归因不清的改动并回退 | 提交范围、首错、需求差异、复位轨迹 |
故障诊断:先分开行为差异与结构差异
- 行为侧:查成功、边界、异常、时序和副作用快照;先确认实际差异是否属于接口合同。
- 气味侧:查重复、职责、隐式依赖和耦合的具体位置;不要用“丑”替代风险描述。
- 改动侧:查单步差异、变更范围和需求是否混入;一次性大改会摧毁归因能力。
- 测试侧:查样本、环境、首个失败和接口比较;测试通过仍要检查未覆盖的调用方信号。
如果重构后结果改变,先用同一案例比较基线和首个差异,再看是否混入需求;如果快乐路径通过而边界失败,补齐行为合同;如果耦合难以降低,先隔离依赖再动结构。每次只改变一个假设,并重放可信基线。
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 4.3 烂代码传奇
用外部行为、结构信号和测试证据判断代码改进,并以小步保持合同的实践。
- 锁定行为
记录成功、边界、异常、时序和副作用,形成可反复运行的外部基线。
- 识别气味
把重复、职责、隐式依赖和耦合信号转成具体改进假设。
- 小步改动
一次改变一个结构原因,保持外部行为并让差异可审查、可回退。
- 运行测试
每个小步后运行回归、边界和异常测试,记录环境、结果和首错。
- 比较接口
对比改动前后的成功、错误、边界、时序和副作用,确认外部合同。
练习
练习
问题 1(4.3 烂代码传奇): 为什么“看起来丑”不能直接证明代码需要重写?
问题 2(锁定行为、识别气味、小步改动): 如何安全地把一个过长函数拆成几个职责?
问题 3(运行测试、比较接口): 一次重构后主流程测试通过,但错误类型和日志字段变化,能否直接合并?
本页小结
4.3 烂代码传奇 的关键不是给代码贴审美标签,而是用行为基线、结构气味、小步差异、测试和接口比较证明改进。完成标准是把重构与需求变化分开,定位首个行为偏离,并证明复位后的相同案例回到基线。