用途
本技能用于在保持外部行为不变的前提下,改善代码结构与质量。它强调通过测试和增量改动来保证安全,适用于需要重构代码、消除代码坏味道、提升可读性与可维护性的场景。
核心原则
测试是安全网。没有测试就不要重构。
重构循环
- 确保测试存在且通过
- 做一次小改动
- 运行测试(必须仍然通过)
- 提交(保持改动隔离)
- 重复
每一步都必须可回退。如果测试失败,回退并尝试更小的改动。
重构前检查清单
如果以下任何一项为假,就停止:
- 被重构的代码存在测试
- 所有测试当前通过
- 理解代码做什么
- 外部行为将保持不变
- 有足够时间认真做(不赶工)
如果没有测试:先添加测试,验证测试通过,然后再重构。
何时重构
提示重构的代码坏味道:
可读性问题:
- 长函数(超过 50 行)
- 深层嵌套(超过 3 层)
- 命名不清晰
- 魔法数字
- 复杂条件
可维护性问题:
- 重复(同一代码出现在多处)
- 上帝类(职责过多)
- 特性依恋(方法使用另一个类多于自身)
- 数据泥团(同一组参数被反复传递)
复杂性问题:
- 圈复杂度大于 10
- 依赖过多
- 紧耦合代码
- 难以测试
何时不应重构
- 没有测试(先添加测试)
- 处于截止日期压力下(推迟到以后)
- 代码能工作且可读(不要过度设计)
- 改变外部行为(那不是重构,而是功能或修复)
- 发布前(风险太大)
经典重构手法
提取函数
问题:函数做太多事情。
重构前:一个长函数同时做校验、计算和保存。
重构后:拆分为 validateOrder、calculateTotals、saveOrder 等聚焦函数。
好处:每个函数单一职责,更易测试,更易理解。
提取变量
问题:复杂表达式难以理解。
重构前:if (user.age >= 18 && user.country === 'US' && !user.banned && user.verified)
重构后:拆为 isAdult、isUSResident、hasGoodStanding、canPurchase 等变量。
好处:自文档化,更易调试,更易修改。
内联函数/变量
问题:不必要的间接层,没有增加清晰度。
重构前:getTotal 只是调用 calculateTotalAmount。
重构后:直接内联计算。
何时内联:抽象没有增加价值,反而让代码更难跟随时。
重命名
问题:命名不清晰或误导。
重构前:function proc(d: any)
重构后:function calculateArea(dimensions: Dimensions)
好处:代码自文档化,无需猜测变量含义。
用命名常量替换魔法数字
问题:代码中出现无法解释的数字。
重构前:const tax = subtotal * 0.08
重构后:定义 TAX_RATE、FREE_SHIPPING_THRESHOLD、STANDARD_SHIPPING_COST 等常量。
消除重复
问题:同一代码出现在多处。
重构前:formatUserName、formatAdminName、formatAuthorName 各自重复相同逻辑。
重构后:统一为 formatFullName。
简化条件
问题:复杂的嵌套 if/else。
重构后:使用提前返回、扁平结构,或查找表。
用多态替换条件
问题:类型检查散布在代码各处。
重构后:定义接口,由各类型实现 calculatePrice。
拆分函数
问题:函数试图做太多事情。
重构后:将校验、计算、持久化、通知分离为独立函数。
重构工作流
逐步过程:
# 1. 确保测试通过
npm test
# ✅ 所有测试通过
# 2. 做一次重构改动
# 例如:提取函数
# 3. 立即运行测试
npm test
# ✅ 仍然通过
# 4. 用描述性消息提交
git add .
git commit -m "refactor: extract validateOrder function"
# 5. 重复下一次重构
如果重构后测试失败:
# 选项 1:回退并尝试更小的改动
git reset --hard HEAD
# 做更小、更安全的改动
# 选项 2:调试并修复
# 找到问题
# 修复它
# 再次运行测试
重构策略
童子军规则:“让代码比你发现时更好。” 触碰代码时修复明显问题、改进命名、提取复杂表达式、补充缺失测试、删除注释掉的代码。小改进会累积。
预备性重构:在添加功能前先重构,使功能更容易添加。引用:“让改动变容易,然后做容易的改动。”
机会性重构:工作时顺手修复注意到的问题。修 bug 时清理周边代码,加功能时改进结构,读代码时修正令人困惑的命名。
计划性重构:专门安排时间改善代码健康,如技术债工单、重构冲刺、清理会议。
重构安全清单
每次改动前:
- 测试存在
- 测试通过
- 理解代码做什么
每次改动后:
- 测试仍然通过
- 功能没有改变
- 代码更清晰
- 可以提交
如果测试失败:
- 理解原因
- 修复或回退
- 绝不提交失败的测试
与其他技能集成
重构期间可应用:boy-scout-rule、simplicity-principles、solid-principles、structural-design-principles、test-driven-development、proof-of-work、code-reviewer。
常见重构陷阱
- 没有测试就重构:风险是未察觉地改变行为。解决方案:先添加测试,再重构。
- 一次改动太多:风险是出问题时难以调试。解决方案:一次一个重构,频繁提交。
- 改变行为:风险是行为改变就不是重构。解决方案:测试必须仍然通过,功能不变。
- 过度工程:风险是“重构”后更复杂。解决方案:更简单更好,不要添加不必要的抽象。
- 压力下重构:风险是赶工导致错误。解决方案:推迟到有时间做对时。
衡量重构成功
好的重构结果是:更易理解、更易修改、更易测试、代码行数通常更少、复杂度更低、性能相同或更好、所有测试仍然通过。如果任何测试失败,就不是成功的重构。
工具
自动化重构工具:IDE 重构命令(安全)、重命名变量/函数(安全)、提取方法(安全)、移动文件(安全)。
手动重构:做小改动、频繁测试、每次改动后提交、用版本控制作为安全网。
记住
- 测试优先——没有测试就不重构
- 小步前进——一次一个改动
- 每步之后测试——必须保持绿色
- 频繁提交——每个安全改动都提交
- 行为不变——如果行为改变,就不是重构
重构是在不改变代码做什么的前提下改善结构。