【译文】关于重构的十条戒律
马丁-福勒(Martin Fowler)所著的《重构》(Refactoring)一书介绍了如何重构代码–在不改变代码功能的情况下重组代码库以改进代码。书中提供了一般原则、代码气味列表以及重构方法。最有价值的是重构目录,例如将重复的代码提取到一个通用函数中或封装一个变量。重构目录中的每个条目都解释了重构背后的动机,以及在实际代码中应用重构的示例,作者还指出了常见的陷阱。
虽然我无法在书评中分享整个目录,但我可以分享作者所信奉的重构一般原则:
你应该有一套全面的测试工具
这是重构中最重要的部分。如果没有测试套件,你就不知道何时(而不是是否)破坏了某些东西。例如,当重命名一个函数时,你还必须确保所有对该函数的引用也被更改。如果你有一套覆盖代码库的测试,错误很快就会显现出来。如果没有测试,就不要盲目重构;重构是开始编写测试的最佳时机。在编写测试时,要确保它们在应该失败的时候失败–总是通过的测试毫无用处。
重构应采取小步骤
重构不同于一般的代码重组。重构一次只做一个小改动。当你把许多小的重构组合在一起时,你会得到比每次改动的总和要大得多的东西。就像 Unix 工具一样,目录中的每个重构都设计得很小且可组合。每个重构都保留了程序的现有行为,只做一件事。不要试图走捷径,同时应用许多重构。虽然这样做似乎效率不高,但通过小改动就能保证保留程序的行为,这样你就只需花费很少的时间来调试这些改动。
如果有人说他们的代码在重构的时候坏了好几天,你就可以肯定他们没有重构。
你应经常运行测试
正如实用主义程序员所说”在所有测试运行之前,编码工作尚未完成。如果你经常运行测试,你就会知道你破坏了什么。将需要的测试绑定到热键上,每次重构时都运行这些测试。每隔几分钟运行一个测试子集,覆盖你正在修改的代码,并在每次 git 提交时运行整个测试套件。理想情况下,每次重构都应该是一次单独的提交,完成后可以将它们全部重置为一次提交。这样您就能准确识别是哪次重构导致了问题,并在测试失败时将其回滚。
应使用持续集成
重构涉及在整个代码库中进行许多小改动,例如更改函数的参数。如果您使用的是与主分支相差数周的大型特性分支,就会经常出现难以解决的合并冲突。作者认为你应该采用持续集成技术,在每天结束时将你的分支合并到主线中。这样既能防止分支之间的差异过大,又能确保你的同事知道你的重构。
重构时不得添加额外功能
重构不是为了改变程序的功能,而是为了让代码更易于理解和构建。作者建议采用测试-重构-代码的循环方式,其中每个阶段都相互独立。试图在重构的同时增加功能会给工作带来不必要的复杂性。如果您发现通过重构可以更容易地构建某个功能,那么请先退回您的更改,进行重构,然后再添加该功能。
应经常重构
重构应该是开发方法的一个自动组成部分。如果你在冲刺阶段专门进行重构,你就会发现你有其他优先事项,却永远无法进行重构。在最初发现问题时进行修复,比任由问题恶化要容易得多。重构应该是机会性的,根据需要进行。作者列出了一些重构的机会:
- 准备性重构:如果对代码进行重构,也许添加功能会更容易。利用这个机会重构代码,让功能开发变得简单。
- 理解性重构:如果你很难理解某个代码块,那么你的同事也会很难理解。利用你的新鲜记忆,重构代码,使其易于阅读。
- 清理重构:开发完一个功能后,花一些时间思考如何重构它。即使你没有时间完全修复它,也要努力让那部分代码比你发现它时更整洁一些。
什么时候不应该重构?如果你能把代码当作一个你不需要理解的 API,那么它就可以保持丑陋。作者还建议,不要重构你没有在积极工作的代码。
应自动重构
许多集成开发环境都提供自动重构功能,例如在整个代码库中重命名一个函数。请利用集成开发环境的自动重构功能,而不是手动重构。即使您使用的是没有重构扩展功能的基本文本编辑器,也可以考虑编写脚本来处理您经常使用的重构文本。即使是自动重构,工具也可能犯错,因此在修改后仍要运行测试。
不可过早优化
某些重构会使程序运行速度变慢。但是,大多数性能优化都会使代码更难阅读,而且大多数代码的运行频率不足以影响性能。取而代之的是,对代码进行剖析,找出运行频率最高的部分,并根据需要对其进行优化。功能完善的软件将更容易优化代码中的热点。需要优化的部分将一目了然,而注释则可以向未来的编辑人员解释优化的原因。您不应该忽视软件的性能,但应专注于重要的部分。
不得使用重复代码
具有相同或非常相似行为的代码块是一种代码气味。如果您必须编辑重复的代码块,那么您也必须编辑它在其他地方出现的代码块。如果您忘记编辑其中一个重复的代码块,很可能会引入一个难以追踪的错误。最简单的情况是,这些代码块完全相同,如开关或 if-else if-else 选择共同值。但是,有些重复代码可能并不完全相同,而是根据参数的不同而行为各异。在判断相似性时,要考虑代码试图做什么,而不是代码是否完全相同。如果发现重复的代码,可将其提取到一个共同的函数或类中,并根据需要使用参数来改变行为。
不得使用过长的函数或过大的类
一段代码越长,就越难理解。冗长的函数或包含大量成员变量的类表明它们试图在同一时间做太多事情。对于大型函数,可以考虑将其分解为多个子函数,作为步骤调用。对于大类,可将常用功能和变量提取到大类的另一个成员类中。您还可以查看类的客户端,看看它们是否只使用了类功能的一个子集。客户使用的每个子集都可以提取到另一个类中。
《重构》是一本思考如何重构程序的好书,虽然有很多观点。我建议每个对这一过程感兴趣的人都买一本这本书,看看目录–它将为你改进自己的程序提供思路。
本文文字及图片出自 The Ten Commandments of Refactoring
你也许感兴趣的:
- 【外评】好的重构与不好的重构
- 【译文】高风险重构
- 【译文】如何坚持长期重构
- 再见了,干净整洁的代码
- 十年“屎山”终重构,但 QQ选用了微软 Teams 放弃的 Electron
- 重构的重构 – 《重构》第二版导读
- 代码重构技巧
- 代码重构的那些坑和实战经验
- [外文翻译]Martin Fowler:机会主义式的代码重构
- 代码审查与重构的5个层次
你对本文的反应是: