Skip to content

重构与 Code Review ​

写代码和改代码是两种能力。重构是「在不改变外部行为的前提下,改善内部结构」; Code Review 是「让另一个大脑在代码进主线前帮你把关」。这篇把两件事讲成可执行的动作。

一、重构:先建立正确认知 ​

重构 ≠ 重写:

  • 重构:小步、行为不变、随时可提交、有测试保护;
  • 重写:推倒重来、周期长、风险高。

什么时候重构(Martin Fowler 的三原则):

  1. 加功能之前:先让结构「准备好」接纳新功能,加起来更省力;
  2. 修 bug 之前:bug 往往藏在坏味道里,先理顺结构再修;
  3. Review 之后:评审指出问题,顺手重构。

什么时候不该重构:临近发布、没有测试、结构烂到不如重写 —— 这三种情况,动它比不动更危险。

二、常见坏味道(Code Smell) ​

坏味道特征手法
长函数一屏看不完、嵌套很深提取函数
大类一个类几十个方法、几千行提取类
重复代码同一段逻辑出现两次以上提取 + 复用
魔法数if (status == 3)命名常量 / 枚举
过长参数列表方法 5 个以上参数引入参数对象
散弹式修改改一个需求要动十几个文件把相关逻辑聚到一个模块
特性依恋一个类频繁访问另一个类的数据把方法搬到数据所在类
数据泥团几个字段总是成群出现提取成对象
大 switch一个 switch 按类型分派逻辑多态 / 表驱动替换
注释掩盖坏代码一段难懂的代码配大段注释用命名解释,删掉注释

三、核心手法(最常用的五个) ​

手法例子
提取函数把「一段有名字的逻辑」抽成方法,用名字代替注释
提取变量复杂表达式抽成有名字的变量
用多态替换条件if (type == "A") ... else if (type == "B") → 策略/子类
用查询替换临时变量临时变量只在三行里用 → 抽成查询方法
引入参数对象f(a, b, c, d) → f(Params p)
java
// ❌ 重构前
double getOrderTotal(Order o) {
    double base = o.items.stream().mapToDouble(i -> i.price * i.qty).sum();
    double discount = o.vip ? base * 0.1 : base * 0.05;   // 魔法数 + 条件
    double shipping = base > 100 ? 0 : 10;
    return base - discount + shipping;
}

// ✅ 重构后
double getOrderTotal(Order o) {
    double base = baseAmount(o);
    double discount = discountFor(o, base);
    return base - discount + shippingFor(base);
}
// ❌ 重构前
double getOrderTotal(Order o) {
    double base = o.items.stream().mapToDouble(i -> i.price * i.qty).sum();
    double discount = o.vip ? base * 0.1 : base * 0.05;   // 魔法数 + 条件
    double shipping = base > 100 ? 0 : 10;
    return base - discount + shipping;
}

// ✅ 重构后
double getOrderTotal(Order o) {
    double base = baseAmount(o);
    double discount = discountFor(o, base);
    return base - discount + shippingFor(base);
}

每一步都小:改一处、跑测试、再改下一处。重构出问题时,退一步就能回到可用状态。

四、重构的安全网:测试 ​

没有测试的重构 = 赌博。流程:

  1. 先给现有代码补「表征测试」(记录当前行为,哪怕没看懂逻辑);
  2. 重构;
  3. 测试全绿 = 行为没变。

一句口诀:重构前先让代码可测,重构时只动结构,重构后看测试是否全绿。

五、Code Review:看什么 ​

维度具体问题
正确性边界、空值、并发、异常路径对不对
安全有没有 SQL 拼接、越权、脱敏缺失
性能有没有 N+1、循环里查库、大对象复制
可读性命名、结构、是否一眼能懂
测试关键路径有没有测、测试是不是只测了 happy path
一致性和项目既有风格、约定一不一致

六、怎么提意见(Review 的沟通) ​

  • 对事不对人:说「这段逻辑有个边界问题」,不说「你写错了」;
  • 区分阻塞和可选:用标签标明「必须改 / 建议改 / 顺口一提」;
  • 给方向不给答案:指出问题,附一句「要不要考虑提取一下」;
  • 一次 Review 别太多:超过 200~400 行,Review 质量断崖式下降 —— 提交要小。

Review 的正确顺序:

  1. 机器先查(格式化、lint、测试、CI)—— 人只看机器查不了的;
  2. 看「改了什么」的 diff,而不是通读全文件;
  3. 重点看:接口边界、数据流、安全、并发。

七、Review 与重构的配合 ​

  • Review 发现坏味道 → 记下来,别在同一个 PR 里既加功能又大改结构;
  • 大型重构要单独一个 PR,只重构、不改行为,Review 起来最省心;
  • 团队约定:重构 PR 和功能 PR 分开。

八、速查 ​

问题答案
重构和重写的区别重构小步、行为不变;重写推倒重来
什么时候重构加功能前、修 bug 前、Review 后
什么时候别重构临近发布、没测试、不如重写
最常见的坏味道长函数、大类、重复代码、魔法数
重构的安全网测试
Review 看什么正确性、安全、性能、可读性、测试
Review 一次多少行200~400 行,再多质量下降
重构怎么提交单独 PR,和功能分开