Skip to content

重构 - 1. 重构,第一个示例

🏷️ 《重构》

最近在看 马丁·福勒Martin Fowler)写的《重构:改善既有代码的设计》(《Refactoring: Improving the Design of Existing Code》)第二版。

第一版应该是 1999 年出版的,第二版是 2018 年底出版的。示例的语言从 Java 换成了 JavaScript ,但其说明的重构方法适用于所有的开发语言。

第一章以一个示例展示了何为重构。书中主要是以代码来说明如何一步步重构的,写的非常详细。这里主要是摘录一些个人认为比较重要的语句,再加上一些自己的理解。

  • 如果你要给程序添加一个特性,但发现代码因缺乏良好的结构而不易于进行更改,那就先重构那个程序,使其比较容易添加该特性,然后再添加该特性。

  • 进行重构的第一个步骤永远相同:确保即将修改的代码拥有一组可靠的测试。

    看到这里感觉第一个步骤就把绝大多数的项目拒之门外了,至少国内来说是这样的。
    工作至今也有十多年了,印象中只在一个日本的银行项目中真正的使用过 JUnit 测试。
    现在公司的测试组也有一小部分自动化的测试,但主要是主流程的结合测试。

  • 在数字时代,软件的名字就是脆弱。

    确实,一个千年虫估计就搞得无数程序员拼命加班了。

  • 重构前,先检查自己是否有一套可靠的测试集。这些测试必须有自我检验能力。

  • 尽管编写测试需要花费时间,但却可以节省下可观的调试时间。构筑测试体系对重构来说太重要了。

  • 无论重构多么简单,养成重构后即运行测试的习惯非常重要。

    这个 多么简单 是指的多简单呢?简单到有可能修改一个变量名都算一次重构,都要在重构后进行测试。
    这个说实话一般人看到这里没有不震惊的,不过为什么要这么做我倒也是可以理解的。
    记得一个项目有一个很小很小的改动,也就一两行代码,看着都应该是正确的,但最后交付的时候还是出现了 BUG(具体的错误原因倒是记不清了,隐约记得就是一个变量名的问题导致的)。

  • 重构技术就是以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。

  • 每次成功的重构后都要提交代码,如果待会不小心搞砸了,便能轻松回滚到上一个可工作的状态。

  • 把代码推送(push)到远端仓库前,把零碎的修改压缩成一个更有意义的提交(commit)。

  • 编码风格:永远将函数的返回值命名为 result ,这样一眼就能知道它的作用。

  • 编码风格:使用一门动态类型语言(如 JavaScript)时,跟踪变量的类型很有意义。参数起名时默认带上其类型名。使用不定冠词修饰它,除非命名中另有解释其角色的其它信息。

  • 傻瓜都能写出计算机可以理解的代码。唯有能写出人类容易理解的代码的,才是优秀的程序员。

  • 好的命名十分重要,但往往并非唾手可得。有了好的名称,就不必通过阅读函数体来了解其行为。

    这同时让我联想到写备注的事情。之前觉得备注写的越详细越好,但是后来发现备注写的过多会增加维护的工作量,而且万一代码更新了而备注没有同步更新的就更糟了。很多地方可以通过有意义的变量名/函数名来实现同备注相同的功能,但有时候取一个合适的名称也是件很头疼的事情。

  • 对于重构过程中的性能问题,建议是:大多数情况下可以忽略它。如果重构引入了性能损耗,先完成重构,再做性能优化。

  • 传参时,尽量保持数据不可变(immutable) -- 可变状态会很快变成烫手的山芋。

    JavaScript 中常使用如下方式创建一个浅拷贝,然后将这个拷贝传递给函数,以免函数体中改变原对象的值。

    javascript
    Object.assign({}, aObj);
  • 重构后,虽然代码的行数增加了,但重构也带来了代码可读性的提高。

  • 虽然说以简为贵,但可演化的软件却以明确为贵。

  • 编程时,需要遵循营地法则:保证你离开时的代码库一定比来时更健康。

  • 完美的境界很难达到,但应该时时都勤加拂拭。

  • 一般来说,重构早期的主要动力是尝试理解代码如何工作。

  • 好代码的检验标准就是人们是否能轻而易举的修改它。

  • 开展高效有序的重构,关键的心得是:小的步子可以更快前进,请保持代码永远处于可工作的状态,小步修改累积起来也能大大改善系统的设计。


摘自:《重构:改善既有代码的设计》 -- 马丁·福勒(Martin Fowler