# 《重构-改善既有代码的设计第二版》

# 导读

这是我看重构2积累下来的心得体会,并不适合所有人,我也只记录常用的重构手法与思想。

这本书写的挺好的,但是建议有以下基础的人阅读

  1. 看过设计模式并实际运用过(涉及到设计模式)。
  2. 在编码时经常写测试(涉及到自测试)。
  3. 至少有1年经验(能看懂js的api与机制)。
  4. 写过OOP相关的项目(涉及到class等)。

# 第一章 重构,第一个示例

  1. 需求来的时候,一般是先设计后编码,但是总不会有十全十美的设计,所以重构就可以来解决这些问题。
  2. 在需要重构时,一般是把功能先写完整测试也不能少,最后在进行重构。
  3. 大多数情况下重构并不会减少代码量,反而会增加代码量,但是会换取较高的可读性、可维护性等。
  4. 重构时可以不用先考虑性能问题,例如大量计算、循环等,如果遇到性能问题的时候,那么重构完成以后在进行性能的调优。
  5. 重构可以先从核心代码进行,减少作用域内的临时变量。

# 第二章 重构的原则

  1. 重构不应该很大,而是应该小
  2. 重构的代码大多数行为会不变,但是也会出现极少数情况。
  3. 两顶帽子: 新功能重构
  4. 消除重复代码,确定所有事物和行为在代码中只表述一次,这正是优秀设计的根本。
  5. 重构可以更快速地开发程序。
  6. 通过投入精力改善内部设计,增加了软件的耐久性,从而可以更长时间地保持开发的快速。
  7. 事不过三,三则重构
  8. 长函数拆分成短函数分离逻辑,如果能保证单一性就更好了。
  9. 重构应该跟随功能进行,有可能会花很长时间,但是又有紧急任务需要完成,那么能花费一点时间来清理,通常是值得了。
  10. 肮脏的代码必须重构,但漂亮的代码也需要很多重构,因为需求是有变化的。
  11. 每次要修改时,首先令修改很容易(警告:这件事有时会很难),然后再进行这次容易的修改。
  12. 发现一堆凌乱的代码,如果不需要它的话就不用重构,只有当需要理解工作原理时,重构才会有价值。
  13. 自测试代码是极限编程的一个重要组成部分。
  14. 自测试代码、持续集成、重构有很强的协同能力,这是敏捷开发的基础。
  15. 除了对性能有严格要求的实时系统,其他任何情况下“编写快速软件”的秘密就是:先写出可调优的软件,然后调优它以求获得足够的速度
  16. 哪怕你完全了解系统,也请实际度量它的性能,不要臆测。臆测会让你学到一些东西,但十有八九你是错的。

# 第三章 代码的坏味道

  1. 对于庞大的switch语句,其中的每个分支都应该通过提炼变成独立的函数调用。如果有多个switch语句基于同一个条件 进行分支选择,就应该使用以多态取代条件表达式。
  2. 循环:你应该将循环和循环内的代码提炼到一个独立的函数中。如果你发现提炼出的循环很难命名,可能是因为其中做了几件不同的事。请勇敢地使用拆分,将其拆分成各自独立的任务。
  3. 编程方式:函数式编程——完全建立在 数据永不改变 的概念基 础上:如果要更新一个数据结构,就返回一份新的数据副本,旧的数据仍保持不变。
  4. 发散式变化:某个模块经常因为不同的原因在不同的方向上发生变化,这就是需要拆分的提示。
  5. 霰弹式修改:每遇到某种变化,你都必须在许多不同的类内做出许多小修改,这就是需要拆分的提示。
  6. 如果在继承时,不需要用超类的接口而且又不实现,那么这种继承关系可能并不需要。
  7. 当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余,如果不知道该怎么做,那么这才是使用注释的良好时机。

# 第四章 构筑测试体系

  1. 确保所有测试都完全自动化,让它们检查自己的测试结果。
  2. 一套测试就是一个强大的bug侦测器,能够大大缩减查找bug所需的时间。
  3. TDD(测试驱动开发):撰写测试代码的最好时机是在开始动手编码之前。需要添加特性时,先编写相应的测试代码。编写测试代码其实就是在问自己:为了添加这个功能,我需要实现些什么?编写测试代码还能把注意力集中于接口而非实现(这永远是一件好事)。预先写好的测试代码也为工作安上一个明确的结束标志:一旦测试代码正常运行,工作就可以结束了。

# 第五章 介绍重构名录

  1. 名称
  2. 速写
  3. 动机
  4. 做法
  5. 范例

# 第六章 第一组重构

  1. 浏览一段代码,理解其作用,然后将其提炼到一个独立的函数中,并以这段代码的用途为这个函数命名。
  2. 应该始终遵循性能优化的一般指导方针,不用过早担心性能问题。
  3. 小函数得有个好名字才行,所以必须在命名上花心思
  4. 改进函数名字的好办法:先写一句注释描述这个函数的用途,再把这句注释变成函数的名字。
  5. 修改函数参数列表不仅能增加函数的应用范围,还能改变连接一个模块所需的条件,从而去除不必要的耦合。

# 第七章 封装

  1. 尝试把动态数据的声明组合成类进行使用。
  2. 保证提取出来的类的单一性。
  3. 如果类的职责出现了不明确的行为,那么请尝试再次提取,保证类的单一性。
  4. 避免封装的源数据直接返回,因为如果客户端更改的话会影响其他代码,如果不是源数据不是很多的话,建议返回源数据的副本。
  5. 如果临时变量是有意义的话,可以单独提取成方法。

# 第八章 搬移特性

  1. 如果修改一条记录时, 总是需要同时改动另一条记录,那么说明很可能有字段放错了位置。
  2. 如果 我更新一个字段时,需要同时在多个结构中做出修改,那也是一个征兆,表明该 字段需要被搬移到一个集中的地点,这样每次只需修改一处地方。
  3. 正常情况下让存在关联的东西一起出现,可以使代码更容易理解。
  4. 可以尝试把循环代码改为管道代码(链式调用api)。

# 第九章 重新组织数据

  1. 保证变量在同一个上下文中的单一性,即每个变量只承担一个责任
  2. 将引用对象改为"值"对象: 在进行类属性赋值时候 可以把可变对象重构为不可变对象,保证数据永远都是新数据。
  3. 将值对象改为引用对象: 如果一组列表中 列表项的属性是重复的(类似于订单列表中的 下单人ID), 那么可以单独提取出来成为一个对象来引用,要不然每次去修改的时候会去遍历修改,而且一个没有修改到就会导致数据不一致的BUG。

# 第十章 简化条件逻辑

  1. 使用卫语句取代嵌套条件表达式。
  2. 多态取代条件表达式。
  3. 使用断言assert, 不要滥用断言。断言不应该用来检查所有“我认为应该为真”的条件,应该只用来检查“必须为真”的条件。

# 第十一章 重构API

  1. 明确表现出“有副作用”与“无副作用”两种函数之间的差异,是个很好的想法。
  2. 如果遇到一个既有返回值又有副作用的函数,试着将查询动作从修改动作中分离出来。
  3. 尽量移除标记参数(直接传入字面量值来影响函数内部控制流), 可多写一些函数来调用。
  4. 透明性:不论任何时候,只要传入相同的参数值,该函数的行为永远一致。
  5. 可以使用命令模式来代替某些参数很多的函数。

# 第十二章 处理继承关系

  1. 如果子类的属性或方法有相同的,可以提升到超类。
  2. 如果超类的一些函数对子类并不适用,就说明不应该通过继承来获得超类的功能。
最后更新时间: 5-16-2023 17:33:52