2026-06-08 / 极限编程

重构:把可修改性变成日常能力

很多团队会把重构当成“有空再做”的事情,但 XP 和《重构》的思想都不是这样。重构不是额外工作,而是 在不改变可观察行为的前提下,让代码更容易理解、更容易改 。

极限编程TDD重构

重构:把可修改性变成日常能力

很多团队会把重构当成“有空再做”的事情,但 XP 和《重构》的思想都不是这样。重构不是额外工作,而是在不改变可观察行为的前提下,让代码更容易理解、更容易改

flowchart LR
    A["现有代码"] --> B["识别坏味道"]
    B --> C["小步修改"]
    C --> D["测试通过"]
    D --> E["结构改善"]

为什么要重构

重构不是为了“优雅”,而是为了让软件保持可修改性。它至少带来四个直接收益:

  1. 改进设计。
  2. 提高可理解性。
  3. 帮助找到 Bug。
  4. 提高后续编程速度。

如果一个系统越改越慢,通常不是“需求太复杂”,而是代码结构已经不再支持变化。

重构和加功能要分开,但又不能完全分开

《重构》里有个非常实用的观点:添加功能和重构是两顶帽子。

  • 加功能时,目标是改变行为。
  • 重构时,目标是不改变行为,只改善结构。

但现实工作里,这两件事常常穿插进行。正确做法不是一次做完一切,而是:

  1. 先通过测试固定行为。
  2. 再用小步重构改善结构。
  3. 每一步都跑测试确认没有回归。

三次法则

原始笔记提到的“三次法则”很适合用来判断是否该重构:

  • 第一次做,先做出来。
  • 第二次做,开始感觉重复。
  • 第三次做,应该考虑提炼抽象。

这个法则很朴素,但非常实用。它帮你避免过早抽象,也帮你避免重复代码积累成腐蚀性的坏味道。

代码坏味道是什么

常见坏味道包括:

  • 重复代码。
  • 过长方法。
  • 大类。
  • 过长参数列表。
  • 条件逻辑过于复杂。

这些问题的共同点是:它们不是 bug,但它们会让 bug 更容易出现

flowchart TD
    A["坏味道"] --> B["重复"]
    A --> C["长方法"]
    A --> D["大类"]
    A --> E["长参数"]
    B --> F["更难维护"]
    C --> F
    D --> F
    E --> F

为什么先有测试再重构

重构最大的阻力不是“不会改”,而是“怕改坏”。所以在重构之前,必须有可靠的自检机制。

没有测试时,重构会变成:

  • 改一点,怕坏。
  • 再改一点,怕漏。
  • 最后不敢动。

有测试时,重构才会变成真正的小步迭代。

实际例子:把判断逻辑提炼出去

原始笔记里的很多例子都在讲一个模式:把复杂条件提炼成更小的函数,把大方法拆成小方法。

csharp
public string GetDisplayName(int period, string name)
{
    var prefix = period switch
    {
        21 => "【Q1】",
        22 => "【Q2】",
        23 => "【Q3】",
        24 => "【Q4】",
        _ => "【未知】"
    };

    return prefix + name;
}

这类提炼的意义不只是“看起来短”,而是让每个函数表达一个清晰意图。

重构和设计模式是什么关系

重构和设计模式不是对立的。设计模式常常可以看作经过反复重构后,沉淀下来的常用结构。

但要注意:

  • 不是为了模式而重构。
  • 也不是为了抽象而抽象。
  • 先让代码能工作,再让结构支持变化。

经典练习为什么有用

BugZero镶金玫瑰 这类练习,本质上是在训练你识别坏味道、拆分职责、保留测试、逐步重构。

它们的价值不在题目本身,而在于把“修改可修改性”变成肌肉记忆。

历史演进

  • 早期团队更多关心“能不能跑”。
  • 随着需求变化变快,人们开始意识到“能不能改”同样重要。
  • 重构从少数人的技巧,逐渐变成团队日常能力。
  • XP 把重构和测试、CI 放在一起,形成闭环。

最佳实践

  • 不要在没有测试时大规模重构。
  • 每次重构都只做小步变化。
  • 先识别坏味道,再选合适的重构手法。
  • 不要为了“美观”而破坏行为边界。
  • 当重复开始出现第三次,就该认真考虑抽象了。
重构:把可修改性变成日常能力 | Remi Resume