重构:改善既有代码的设计(第2版)
软件工程领域的经典著作,作者马丁·福勒是ThoughtWorks首席科学家。全书系统介绍了代码重构的原则、方法和实践,涵盖数十种重构手法和代码坏味的识别。本书是程序员提升代码质量的必读经典,被全球软件开发团队广泛采用。
本书速读
📖 本书核心内容
《重构》是软件工程领域最具影响力和实用性的经典著作,第2版于2018年出版,使用JavaScript作为示例语言。
作者马丁·福勒是ThoughtWorks首席科学家,全球知名的软件开发思想家和实践者。他在软件开发领域拥有超过三十年的经验,是敏捷宣言的签署者之一。
全书系统介绍了代码重构的原则、方法和实践,涵盖数十种重构手法和代码坏味的识别。
本书是程序员提升代码质量的必读经典,被全球软件开发团队广泛采用。
福勒的核心理念是:重构是在不改变代码外部行为的前提下改善其内部结构的过程。重构不是一次性的大规模修改,而是小步快跑的持续改进。
书中通过一个完整的案例,展示了如何将一段凌乱的代码逐步重构为清晰、可维护的代码。这个案例贯穿全书,帮助读者理解每种重构手法的应用场景和具体步骤。
重构的价值不仅在于改善代码质量,更在于提高开发效率。整洁的代码更容易理解和修改,从而使团队能够更快地响应需求变化。
本书的第2版全面更新了示例代码和重构手法,使其更适合现代JavaScript开发实践。
🎯 重构的核心原则:小步快跑
重构的第一原则是保持小步和安全的节奏。
福勒将重构定义为在不改变代码外部行为的前提下改善其内部结构的过程。重构不是添加新功能,也不是修复bug,而是让代码变得更容易理解和修改。重构的目的是降低技术债务,提高代码的可维护性和可扩展性。重构是软件开发中不可或缺的日常活动,而不是偶尔为之的特殊任务。
福勒提出了两顶帽子的比喻——当你在添加新功能时,你戴着添加功能的帽子,不应该进行重构。当你在重构时,你戴着重构的帽子,不应该添加新功能。这两顶帽子不能同时戴,因为同时做两件事会增加出错的风险。通过明确区分这两种活动,你能够更加专注和高效地完成每一项任务。
重构应该是小步的、频繁的、安全的。每一步重构都应该是一个微小的、可验证的改变,比如重命名一个变量、提取一个函数、移动一段代码。小步重构的优势在于,如果某一步引入了错误,你能够快速地定位和修复。大步重构的风险在于,如果引入了多个错误,你将很难定位问题的根源。
福勒指出,重构的前提是拥有可靠的测试套件。测试套件能够在重构过程中提供安全保障——如果某一步重构破坏了代码的行为,测试会立即告诉你。没有测试保护的重构就像在没有安全网的情况下走钢丝。在重构之前先确保测试覆盖率足够高,然后在每一步重构后都运行测试来验证代码的正确性。
测试是重构的安全网,没有测试的重构是危险的。福勒建议在开始重构之前,先检查和补充测试用例,确保关键路径和边界条件都被覆盖。只有在测试的保护下,你才能放心地进行重构,而不必担心引入隐蔽的bug。
🎯 代码坏味:识别需要重构的信号
代码坏味是福勒提出的识别需要重构代码的信号。
神秘命名是指变量、函数或类的名称不能清晰地表达其用途和含义。当读者看到一段代码时,如果不能通过命名快速理解代码的意图,那就是神秘命名的坏味。神秘命名会降低代码的可读性,增加理解和修改的成本。解决神秘命名的方法是重命名——使用清晰、准确、具体的名称来替代模糊和含糊的名称。
重复代码是指相同的或相似的代码片段在多处出现。重复代码会导致维护成本增加——当需要修改逻辑时,你必须找到所有的重复代码并进行相同的修改。如果遗漏了某一处,就会引入bug。解决重复代码的方法是提取函数——将重复的代码提取为一个独立的函数,然后在多处调用这个函数。
过长函数是指承担了过多职责的函数。福勒认为,函数越长,越难以理解和修改。过长的函数往往包含了多个逻辑层次,混合了高层业务逻辑和底层实现细节。解决过长函数的方法是提取函数——将函数中的逻辑块提取为独立的、职责单一的函数。
过长类是指承担了过多职责的类。一个类如果包含了太多的字段和方法,就会变得难以理解和维护。解决过长类的方法是提取类——将相关的字段和方法组合成新的类,让每个类都专注于自己的职责。如果某个类的实例变量或方法子集具有高度的内聚性,就应该考虑将其提取为独立的类。
过长的参数列表是另一种常见的代码坏味。当函数的参数数量过多时,函数的理解和使用成本会显著增加。解决过长参数列表的方法是引入参数对象——将相关的参数组合成一个对象,然后将这个对象作为参数传递给函数。参数对象不仅能够减少参数的数量,还能够提高参数的内聚性和可维护性。
全局数据是指那些在整个系统中都可以被访问和修改的数据。全局数据的问题是它可以在任何地方被修改,这使得调试和维护变得困难。解决全局数据的方法是封装——将全局数据封装在类或模块中,通过接口来访问和修改。封装能够限制数据的访问范围,提高代码的可控性和可测试性。
🎯 核心重构手法:工具箱中的利器
福勒介绍了数十种重构手法,以下是几种最常用和最重要的。
提取函数是最基础也最常用的重构手法。当你发现一段代码可以独立为一个逻辑单元时,将其提取为一个独立的函数。提取函数能够提高代码的可读性和可复用性。福勒建议函数的命名应该清晰地表达其用途,让读者不看函数体就能理解函数的作用。提取函数的步骤包括:创建新函数、复制代码到新函数、处理局部变量、替换原代码为函数调用、运行测试。
内联函数是提取函数的逆操作。当一个函数的实现比其名称更加清晰时,或者当函数的调用者只有一个时,可以考虑将其内联。内联函数能够减少不必要的间接层,使代码更加直接和易于理解。内联的步骤包括:确保函数没有多态性、找到所有调用点、用函数体替换调用、删除原函数、运行测试。
搬移函数是指将函数从一个类或模块移动到另一个类或模块。当你发现一个函数与其当前所在的类或模块的关联度不如与另一个类或模块的关联度时,可以考虑搬移这个函数。搬移函数能够提高代码的内聚性,使每个类或模块都专注于自己的职责。搬移的步骤包括:检查函数是否引用了源类的私有成员、处理这些引用、在目标类中创建新函数、将代码复制到新函数、修改调用点、运行测试、删除源类中的原函数。
拆分阶段是指将一段混合了多个逻辑阶段的处理拆分为多个独立的阶段。当你发现一段代码中同时处理数据转换和业务逻辑时,可以考虑将其拆分为两个独立的阶段。拆分阶段能够降低代码的复杂度,使每个阶段都专注于自己的任务。福勒建议通过函数提取和搬移来实现阶段的拆分。拆分阶段的判断标准是:如果代码中有一部分在处理数据转换,另一部分在处理业务逻辑,就应该考虑拆分。
🎯 重构与架构:从代码到系统
福勒在第二版中新增了对重构与架构关系的讨论。
重构不仅仅是代码层面的活动,也与系统架构密切相关。通过持续的重构,你可以发现和改善系统中的架构问题——比如模块之间的紧耦合、职责分配不合理、数据流不清晰等。重构是架构演化的重要手段,它使架构能够在不中断开发的情况下逐步改进。架构设计不应该是一次性的活动,而应该是随着系统的发展而不断演化的过程。
福勒将代码坏味和技术债务联系起来。技术债务是指为了快速交付而做出的短期妥协,这些妥协会在未来产生维护成本。重构是偿还技术债务的主要方式。福勒建议将重构纳入日常开发流程,而不是等到技术债务积累到无法承受时才进行大规模的重构。就像财务债务一样,技术债务需要定期偿还,否则利息会越积越多。
重构不仅仅是一种技术手段,更是一种团队文化。在重视重构的团队中,每个成员都有责任保持代码的清洁和健康。代码审查、结对编程和持续集成等实践都能够促进重构文化的建立和传播。重构文化的核心信念是:代码质量是每个人的责任,而不仅仅是某个特定角色的责任。当团队中的每个人都致力于保持代码的整洁时,整个项目的质量会得到显著提升。
⭐ 金句摘录
重构是在不改变代码外部行为的前提下改善其内部结构的过程。
重构不是一次性的大规模修改,而是小步快跑的持续改进。
如果你需要花时间来理解一段代码,那这段代码很可能需要重构。
重构不仅仅是一种技术手段,更是一种团队文化。
📚 阅读建议
适合所有阶段的程序员阅读,从初学者到架构师都能从中受益。
建议阅读后在实际代码中练习重构手法,从简单的小步重构开始,逐步掌握更复杂的技巧。
重点阅读代码坏味和核心重构手法章节,这两部分对提升代码质量最具实用价值。