《重构——改善既有代码的设计》感想

1,547 阅读6分钟

为什么会拾起这本书来研读呢?一方面是因为近期在项目的更换中发现两个问题

  1. 读懂别人的代码不易,特别是在工程复杂的时候
  2. 自己写的代码能否让别人轻易读懂,包括后续的维护是否简易、扩展性是否足够。
  3. 写出让机器读懂的代码很正常,写出让别人轻易读懂的代码,才是优秀的。

手头上刚好有这么本书,希望能够在阅读和做总结的过程中,能不断严厉要求和规范自己,让自己的代码赏心悦目。得到近期荒废之后另一个进步。PS,估计目前只简记心得,后续如果有时间可以另外尝试分享现实经验。

《重构——改善既有代码的设计》(第二版)

前情,重构代码的一些抽象关键点

  • 重构代码的步骤
  1. 确保有可靠的测试集,保证自己每一处修改可编译且不会引起新增问题
  2. 每一步都是粒度最小的改动,commit本地,一旦发现问题,可以最快的找到最后的改动点,避免在多步骤的改动后无法快速定位引起问题的改动点。
  3. 拆分,注意命名、变量、函数、多态。
  • 重构不应该使原有功能面目全非,而是结构化调整。
  • 两顶帽子 —— 添加新功能 & 重构,两者独立,但可以经常性交替。经常性地对代码进行重构,可以保证代码框架足够清晰,不会因为时间堆积而让代码越来越让人看不懂。
  • 只要想重构,就需要有一套可以自测试的代码,来快速发现错误、避免错误。
  • 重构有效的关键及协同效应。并且需要自我判断,是否需要重构。
  1. 自测试代码
  2. 持续集成
  3. 重构
  • 重构影响性能,那么就关注在影响性能热点的小部分代码上(10%),让他发挥最大效用。

如何重构代码,什么代码需要重构

  • 命名不规范
  • 重复的代码
  • 过长的函数
  • 过长的参数列表 —— 参数的减少
  • 全局变量引起的莫名bug & 可变数据 —— 封装变量
  • Fancy Evny —— 策略模式和访问者模式
  • 这里居然讲了24种,包括最后的注释,但慢慢地,看到中间部分,发现其实简单的重构方法,自己在平时项目中也有形成这种习惯了。

养成自测试的习惯

  • 应该在开始编写模块的时候就写自测试代码,后续当你改动时,就可以通过这些代码保证改动的有效和准确
  • 正常路径的测试,边界路径的条件也需要考虑(这个其实在自己的实践中经常被故意忽略,后续要养好习惯才行,如果你注意到边界行为,那么就需要去验证。)

一些重构的方法(作者的名录),从动机和做法出发

  • 提炼函数
  • 内联函数
  • 提炼变量(这种在使用表达式较长的时候,可以提炼变量,使代码更容易阅读)
  • 内联变量(当变量本身足够清楚表达含义时,就不需要提炼变量了)
  • 改变函数声明
  • 封装变量
  • 引入参数对象(当多个函数使用了相同的参数时)
  • 函数组成类(共同的函数作用范围,可以简单理解为当一些方法是有相同的共性的时候,比如提取一些工具类的方法)
  • 函数组合成变换(需要派生信息的时候,enrich something, 当你的输入会得到一个变化的输出(同个对象,变化了)
  • 拆分阶段

封装

  • 封装记录,封装变量、封装到函数或者类中
  • 以对象取代基本类型
  • 添加中间人 & 移除中间人 (委托关系是否必要)
  • 替换算法

搬移特性

  • 搬移函数、搬移变量
  • 搬移语句到函数中 <--> 搬移语句到调用者中
  • 以函数调用取代内联代码
  • 合并重复语句
  • 拆分循环(让一个循环只做一件事情)
  • 以管道取代循环(Replace Loop with Pipeline)【这个Colletion PipeLine的概念比较少使用到,后面关注】
  • 移除死代码(如果是团队协同,那么就指操作自己部分的代码吧!想起了还没毕业的时候,第一次写一个业务需求,将别人的代码改动得乱糟糟,哈哈哈哈哈)

重新组织数据

  • 拆分变量(移除对变量的赋值)
  • 字段改名
  • 以查询取代派生变量(Replace Derived Variable with Query)
  • 将引用对象改为值对象 <--> 将值对象改为引用对象

简化条件逻辑

  • 分解条件表达式
  • 合并条件表达式
  • 以卫语句取代嵌套条件表达式(Replace Nested Conditional with Guard Clauses)
  • 以多态取代条件表达式
  • 引入特例
  • 引入断言(在目前的实际项目中,很少在应用层使用断言,一般在单元测试中使用)

重构API

  • 将查询函数和修改函数分离(任何有返回值的函数都应该没有副作用)
  • 函数参数化
  • 移除标记参数(感觉是func test(A,B) -- > setA(B)
  • 保持对象完整
  • 以参数代替查询 -- 以查询代替参数
  • 移除设置函数(如果不希望参数可以被改变,那就去掉setter吧)
  • 以工厂函数取代构造函数
  • 以命令取代函数(以函数对象取代函数) -- 以函数取代命令

处理继承关系

  • 函数上移(下移)(子类中有相同的方法,可以移动到父类中)
  • 字段上移(下移)
  • 构造函数上移(下移)
  • 以子类代替类型码(移除子类)
  • 提炼超类 Extract Superclass
  • 折叠继承体系(当子类和超类无太大区别)
  • 以委托取代子类
  • 以委托取代超类

其实好像是本书的目录结构,加上了一些自己的理解,也方便自己查看 (有些翻译很生涩,后续有空再仔细解说) 在记录总结的过程中,发现其实很多习惯已经在实际项目中使用且注意到了,欣慰。当然后面得更好地让自己养成习惯才好。

以上。