2026-06-08 / 极限编程
重构:把可修改性变成日常能力
很多团队会把重构当成“有空再做”的事情,但 XP 和《重构》的思想都不是这样。重构不是额外工作,而是 在不改变可观察行为的前提下,让代码更容易理解、更容易改 。
极限编程TDD重构
重构:把可修改性变成日常能力
很多团队会把重构当成“有空再做”的事情,但 XP 和《重构》的思想都不是这样。重构不是额外工作,而是在不改变可观察行为的前提下,让代码更容易理解、更容易改。
flowchart LR
A["现有代码"] --> B["识别坏味道"]
B --> C["小步修改"]
C --> D["测试通过"]
D --> E["结构改善"]
为什么要重构
重构不是为了“优雅”,而是为了让软件保持可修改性。它至少带来四个直接收益:
- 改进设计。
- 提高可理解性。
- 帮助找到 Bug。
- 提高后续编程速度。
如果一个系统越改越慢,通常不是“需求太复杂”,而是代码结构已经不再支持变化。
重构和加功能要分开,但又不能完全分开
《重构》里有个非常实用的观点:添加功能和重构是两顶帽子。
- 加功能时,目标是改变行为。
- 重构时,目标是不改变行为,只改善结构。
但现实工作里,这两件事常常穿插进行。正确做法不是一次做完一切,而是:
- 先通过测试固定行为。
- 再用小步重构改善结构。
- 每一步都跑测试确认没有回归。
三次法则
原始笔记提到的“三次法则”很适合用来判断是否该重构:
- 第一次做,先做出来。
- 第二次做,开始感觉重复。
- 第三次做,应该考虑提炼抽象。
这个法则很朴素,但非常实用。它帮你避免过早抽象,也帮你避免重复代码积累成腐蚀性的坏味道。
代码坏味道是什么
常见坏味道包括:
- 重复代码。
- 过长方法。
- 大类。
- 过长参数列表。
- 条件逻辑过于复杂。
这些问题的共同点是:它们不是 bug,但它们会让 bug 更容易出现。
flowchart TD
A["坏味道"] --> B["重复"]
A --> C["长方法"]
A --> D["大类"]
A --> E["长参数"]
B --> F["更难维护"]
C --> F
D --> F
E --> F
为什么先有测试再重构
重构最大的阻力不是“不会改”,而是“怕改坏”。所以在重构之前,必须有可靠的自检机制。
没有测试时,重构会变成:
- 改一点,怕坏。
- 再改一点,怕漏。
- 最后不敢动。
有测试时,重构才会变成真正的小步迭代。
实际例子:把判断逻辑提炼出去
原始笔记里的很多例子都在讲一个模式:把复杂条件提炼成更小的函数,把大方法拆成小方法。
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 放在一起,形成闭环。
最佳实践
- 不要在没有测试时大规模重构。
- 每次重构都只做小步变化。
- 先识别坏味道,再选合适的重构手法。
- 不要为了“美观”而破坏行为边界。
- 当重复开始出现第三次,就该认真考虑抽象了。