书 · 1999-06 · 马丁·福勒(Martin Fowler)

《重构:改善既有代码的设计》

计算机与 AI书单第 22 名

一句话结论

把设计改进拆成可验证的小步骤,让已经能运行的软件继续保持容易理解、容易修改。

值不值得读完整本?

值得读完整本,尤其适合已经写过程序、开始承担维护工作的人;但目录部分应分批练习和查阅,不必连续读完所有代码细节。

  • 先精读第1章,跟着录像带租赁程序的例子操作,再读第2至4章,弄懂重构的边界、坏味道和测试的作用。
  • 第5章介绍目录的使用方式;第6至11章先通览,再练习提炼与内联方法、搬移方法、封装数据、简化条件、调整方法接口和继承关系。
  • 第12章值得读,它说明较大的设计调整如何由一系列小重构组成;第13至15章补充实际采用、工具和整体认识,第15章尤其值得细读。
  • 第14章涉及的具体工具已有年代感,可以略读;练习时可以使用现代工具,但要能解释每一步为什么保持行为。
  • 准备考试时,重点掌握六条核心思想及其适用条件,不必背下所有重构名称;最好亲手完成一次有测试保护的修改。

核心观点

1. 重构保持外部行为,并与功能修改分开

重构是在保持软件外部可观察行为的条件下改善内部结构。增加功能、修复错误和重构可以交替进行,但应分清当前正在做哪一种工作。

为什么:如果同时改变结构与功能,出现差异时就难以判断它来自新需求还是整理过程中的错误。保持行为这一约束,为每次修改提供了明确的检查标准。把两种工作分开,可以暂时只处理结构问题,减少需要同时考虑的因素。整理后的代码再承接功能变化,通常也更容易定位修改位置。

2. 用测试与小步变换控制风险

重构之前要有能够自动检查结果的测试,每次只做小幅修改,并频繁编译和运行测试。安全来自持续反馈,而不是一次性想清全部变化。

为什么:测试保存了对已有行为的检查,使开发者不必每次都靠人工重新确认。改动越小,测试失败时需要排查的范围就越窄。频繁反馈让错误在尚未叠加其他修改时暴露,降低诊断和撤回的成本。测试并不能穷尽所有情况,因此仍须理解代码、检查依赖和遵守操作前提。

3. 坏味道是调查线索,不是机械判决

重复代码、过长方法、发散式变化和霰弹式修改等现象,可以提示设计哪里值得检查。它们帮助提出问题,不能代替对具体代码和修改需求的判断。

为什么:结构问题往往先表现为维护困难,而不是立即产生错误。例如,同一规则散落多处,会让一次需求变化需要同步修改多个位置,也更容易漏改。坏味道把这类经验变成容易识别的线索,再引导读者查找合适的重构。不同情境需要不同结构,所以书中也提供方向相反的操作,而不是要求代码始终变短或类始终变多。

4. 让名称、方法和接口直接表达意图

代码应帮助读者看懂它在做什么,而不只是让机器执行。提炼方法、改善命名、整理参数以及分离查询与修改,都是减少理解负担的手段。

为什么:一大段实现细节会迫使读者在脑中重新推导它的目的。把有独立含义的操作提炼出来并准确命名,可以让读者先理解整体,再按需查看细节。接口如果混合查询与状态修改,调用者还必须记住隐藏的副作用。反过来,没有提供额外含义的方法也可能增加跳转负担,因此内联同样是改善表达的工具。

5. 围绕变化重新分配职责

方法和数据应放在合适的对象中,让相关规则集中,让其他部分少知道实现细节。反复按类型分支的逻辑,可以在合适时通过多态承接变化。

为什么:如果一个方法不断读取另一个对象的数据,它往往把本应局部处理的知识带到了外部。搬移方法、封装数据或提炼类,可以减少这种依赖,使规则变化集中在较小范围。多处重复的类型判断则要求每次新增类型都同步修改多个分支,多态可以把各类型的规则放到各自实现中。继承关系也需要检验:它是否真实适合当前变化,还是应该改用委托。

6. 持续设计的目标是降低后续修改成本

重构让设计随着对问题的理解而改进,其价值主要在于让后续开发更顺畅。是否重构、何时停止,都应联系实际工作,而不是追求抽象的整洁。

为什么:开发过程中会不断发现最初没有理解的规则,一开始的设计不可能准确预见全部需求。若只能在开工前设计,后来积累的认识就难以进入系统结构。持续重构允许团队利用这些认识,减少重复、依赖和理解成本,再把投入转化为更容易实现的修改。但这不意味着任何整理都划算;性能问题也应通过测量定位,再对热点优化,而不是凭猜测牺牲整个系统的清晰度。

反对意见

拆成更多小方法未必降低复杂度(John Ousterhout)

Ousterhout 在《软件设计的哲学》中强调,模块应以简单接口隐藏足够多的实现复杂度。若拆分只增加名称、接口和调用跳转,却没有隐藏有用的信息,总体理解成本可能上升。这与本书偏爱短方法的取向形成张力,但不意味着他反对重构;他明确批评的方法长度观点主要针对 Robert C. Martin 的《代码整洁之道》。[作者对第二版差异的说明](https://web.stanford.edu/~ouster/cgi-bin/book.php)

错误抽象可能比重复更贵(Sandi Metz)

Metz 在 The Wrong Abstraction 中描述:相似代码被合并后,后续需求可能迫使共享实现不断增加参数和条件,最终把不同概念绑在一起。她建议在抽象已经不合适时,先内联、恢复必要的重复,再寻找正确边界。这反对把消除重复当成优先于一切的目标,也与本书提供内联操作的思路相容。[原文](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction)

行为保持是要求,实际操作仍可能出错(Di Penta、Bavota、Zampetti)

三位研究者在 2020 年的复现研究中分析了 103 个项目,发现重构活动与后续缺陷修复存在关联。不过,人工检查也排除了许多把同次提交中的其他修改或旧缺陷误算到重构头上的情况。其结论是应认真规划和验证实际重构,不能把定义中的行为保持当成操作成功的保证;这也不是重构普遍有害的因果证明。[论文](https://arxiv.org/abs/2009.11685)

相关推荐

导读由 AI 根据公开资料整理,没有读取原书全文,一切以原著为准。导读 Primer 首页