重构阅读笔记一

  1. 重构前需建立可靠快速的测试机制;
  2. 重构的基本技巧:小步前进,频繁测试;

作者在文中提到了很多重构策略,在检索时发现一些比较好的网站:

  1. Refactor Guru https://refactoringguru.cn/replace-parameter-with-explicit-methods
  2. 重构的翻译稿 https://www.kancloud.cn/sstd521/refactor/194285

第一章 重构,第一个案例

  1. 当你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达成目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。
  2. 重构之前,首先检查自己是否有一套可靠的测试机制。这些测试必须有自我检验能力。???如何建立可靠的测试机制???
  3. 重构技术就是以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。
  4. 任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。
  5. 个人补充:重构很大程度上依赖于设计模式。
  6. 重构的节奏:测试,小修改,测试,小修改…..

第二章 重构的原则

  1. 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
  2. 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
  3. 两顶帽子:添加新功能,重构。添加新功能时,你不应该修改既有代码,只管添加新功能,并让测试正常运行;重构时你就不能再添加功能,只管改进程序结构,此时你不应该添加任何测试。
  4. 重构改进软件设计:所有事物和行为在代码中只表述一次,正是优秀设计的根本。
  5. 重构使软件更容易理解:很多时候,未来阅读代码的那个程序员就是自己。
  6. 重构帮助找到BUG
  7. 重构提高编程速度:作者反对专门拨出时间进行重构,重构应该随时随地进行,之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好。
  8. 三次法则:事不过三,三则重构:第一次做某件事只管去做,第二次做类似的事会产生反感,第三次再做类似的事,你就应该重构。
  9. 添加功能时重构:重构的原动力——代码的设计无法帮助我轻松添加我所需要的特性。
  10. 修补错误时重构:显然代码还不够清晰——没有清晰到让你能一眼看出bug。
  11. 复审代码时重构:
  12. 我们希望程序:(1) 容易阅读 (2) 所有逻辑都只在唯一地点指定;(3) 新的改动不会危及现有行为;(4) 尽可能简单表达条件逻辑。
  13. 对于“进度驱动”的产品经理,作者的建议是“不告诉他”
  14. 计算机科学是这样一门科学:它相信所有问题都可以通过增加一个间接层来解决。
  15. 间接层的价值:
    1. 允许逻辑共享(函数调用或函数继承);
    2. 分开解释意图和实现;
    3. 隔离变化;
    4. 封装条件逻辑;
  16. 不要过早的发布接口。请修改你的代码所有权政策,使重构更顺畅。
  17. 何时不该重构:将大块头软件重构为封装良好的小型组件,然后你就可以逐一对组件做出重构或重建的决定。如果项目已经非常接近最后期限,不应该再分心与重构。
  18. 编写快速软件的秘密就是:首先写出可调的软件,然后调整它以求获得足够速度。

第三章 代码的坏味道

  1. 重复代码:方法提炼;超类提炼;模板方法设计模式。
  2. 过长函数:让小函数容易理解的真正关键在于一个好名字。你应该更加积极地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。我们可以对一组甚至短短一行代码做这件事,哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离。
    1. 如何确定该提炼哪一段代码呢?一个很好的技巧是:寻找注释。
    2. 条件表达式和循环也是提炼的信号,每个分支的判断条件与逻辑都可以抽取为独立函数;循环与其内部代码也应该提炼到独立函数中。
  3. 过大的类:合并实体参数
  4. 过长参数列:抽取为对象,抽取为方法。
  5. 发散式变化(一个类受多种变化的影响):某个类经常因为不同的原因在不同的方向上发生变化,此时应该将该类提炼为多个类。针对某一外界变化的所有相应修改,都只应该发生在单一类中。
  6. 霰弹式修改(一个变化引发多个类的相应修改):每当遇到某种变化,你都必须在许多不同的类内做许多小修改。这种情况下你需要提取方法和实体域把所有需要修改的代码放进同一个类。
  7. 依恋情结:函数多某个类的兴趣高过自己所处类的兴趣。最根本的原则是:将总是一起变化的东西放在一块儿。
  8. 数据泥团:两个类中相同的字段,需要函数签名中相同的参数,这些总是绑在一起的数据真应该拥有属于它们自己的对象。
  9. 基本类型偏执:可将原本单独存在的某些数据值替换为对象,从而进入对象世界。(如结合币种和数值的money类,由一个起始值和一个结束值组成的range类,电话号码或邮政编码等特殊的字符串等)
  10. switch惊悚现身:面向对象程序一个最明显特征就是:少用switch语句。大多数时候,一看到switch语句,你就应该考虑以多态来替换它。
  11. 平行继承体系:(当你为某个类增加一个子类,必须也为另一个类相应增加一个子类)一般策略是:让一个继承体系的实例引用另一个继承体系的实例。
  12. 冗赘类:如果一个类的所得不值其身价,它就应该消失。
  13. 夸夸其谈未来性:不过度过早设计。
  14. 令人迷惑的暂时字段:当一个或者某几个参数只在特定算法或特定情况下使用,将它们提取成单独的类。
  15. 过度耦合的消息链:先观察消息链最终得到的对象是用来干什么的,看看能够通过Extract Method把使用该对象的代码提炼到一个独立函数中,再运用Move Method把个函数推入消息链。
  16. 中间人:你也许看到某个类接口有一半的函数都委托给其他类,这样就是过度运用。
  17. 狎昵关系:两个过度耦合的类必须拆散,帮助它们划清界限。可以移动方法,或者将两者共同点提炼到一个新的类中。
  18. 异曲同工的类:如何两个类做同一件事,却有着不同的函数名,则需要重命名。或者将重复代码移入到新的类中。
  19. 不完美的库类:可以引入外加函数或者集成原库类。
  20. 纯稚的数据类:数据类是指它们拥有一些字段,以及用于访问这些字段的函数,初次之外一无长物。这样的类总是被其他类过分细琐地操纵着。应该检查设置它值的函数,将它们移动到该类中。
  21. 被拒绝的馈赠:子类应该继承超类的函数和数据,但如果它们不想或不需要继承,它们得到所有礼物,却只从其中挑选几样来玩。按照传统说法,这就意味着继承体系设计错误。你需要为这个子类建立一个兄弟类,将父类中用不到的域与函数下推到兄弟类中。
  22. 过多的注释:如果你需要注释来解释一块代码做了什么,试试提取函数方法,如果函数已经提炼出来,但还是需要注释来解释其行为,试试重命名函数,如果还是需要注释,试试引入断言。(当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余

第四章 构筑测试体系

  1. 确保所有测试都完全自动化,让它们检查自己的测试结果。
  2. 一套测试就是一个强大的bug侦测器,能够大大缩减查找bug所需要的时间。
  3. 撰写测试代码的最有用时机是在开始编程之前。
  4. 频繁地运行测试。每次编译请把测试也考虑进去——每天至少执行每个测试一次
  5. 每当你收到BUG报告,请先写一个单元测试来暴露bug。
  6. 测试应该是一种风险驱动的行为,测试的目的是希望找出现在或未来可能出现的错误。(所以作者不会测试那些仅仅读或写一个字段的访问函数)
  7. 测试的要诀是:测试你最担心出错的部分。
  8. 编写未臻完善的测试并实际运行,好过对完美测试的无尽等待。
  9. 考虑可能出错的边界条件,把测试火力集中在那儿。
  10. 测试中,积极思考如何破坏代码,这种思维能够提高生产力。
  11. 当事情被认为应该出错时,别忘了检查是否抛出了预期的异常。
  12. 不要因为测试无法捕捉所有的bug就不写测试,因为测试的确可以捕捉到大多数bug。
  13. “花合理时间抓出大多数bug”要好过“穷尽一生抓出所有bug”

第五章 重构列表

作者提示,这些重构建议是有用的,尽管可能是不全面的或者不够好的。