初识

重构的第一步是构建可靠的测试.

好的代码能展示自身意图.

移除不必要的临时变量, 减少来来回回传参带来的困扰; 将效率问题后置.

重构的节奏: 测试=>小改=>测试=>小改=>…

重构的基本原理

对软件内部结构的一种修改 , 在不改变软件外观行为的条件下, 使之更易于理解和修改.

WHY:

  • 改进软件的设计
  • 变得易于理解复杂系统
  • 易于发现bug
  • 利于总体的效率

BAD:

  • 难读的程序很难改
  • 有冗余逻辑的程序很难改
  • 添加新功能时需要修改现有代码的程序很难改
  • 有复杂逻辑的程序很难改

抽象和重构:

  • 共享逻辑
  • 分离意图和实现
  • 隔离变化
  • 条件判断=>多态

代码里的坏味道

重构的警告信号:

  • 重复代码

    方式上移 替换算法 提炼包围 模板方法 etc

  • 方法过长

    小函数带来了抽象的好处: 解释/共享/选择 .

    方法名可以取代注释. 当你想写注释的时候, 就抽象一个小方法出来.

    对于临时变量过多的问题, 可以采用查询替换临时变量/链式调用替换临时变量.

    对于参数过多的问题, 可以采用参数对象/保留完整对象.

    条件/循环 提炼方法.

  • 类太大

    提炼类/提炼模块

  • 参数列表太长

    传递对象而不是参数, 使用方法而不是参数.

  • 发散型变化

    使用多态

  • 霰弹型修改

  • 特性依赖

    谁的数据就该归谁管, 一起变化的东西放在一起.

etc..

构建测试

单元测试是给程序员用的, 主要用来提高生产力.

当收到QA的Bug时, 应该先写一个测试来重现此bug, 再debug.

重构花名册

组织方法

提炼方法

当方法过长, 或者复杂到需要添加注释来解释的时候, 就可能需要提炼方法了.

应该倾向于使用短小, 名称有意义的方法:

  • 容易写, 复杂度低
  • 方法名即注释
  • 细小的颗粒度适于重用

方法名应该说明做了什么, 而不是怎么做; 多用名次, 少用动词.

内联化方法

简单的说就是把方法调用合并回主方法里.

当这个变量名已经很清晰, 不需要单独使用方法时. 好像很少遇到这种情况, 更多的是不需要提前优化. 在不清楚更进一步的需求的时候, 就不需要用方法把实例变量包装起来.

当优化一个巨大方法前, 可以先使用内联方法把散落的方法收集回来, 再重新组织和提炼.

注意内联的方法是否是多态的.

使用查询方法替换临时变量

将抽象出来的查询方法标记为私有方法.

形式上是将一段动作抽取出来, 形成一个查询, 将一系列计算动作转化为一个名词的查询.

方法要说明做了什么, 而不是怎么做的.

链式调用替换临时变量

链式调用要具体区分, 如果他们之间传递了不同类型的对象, 那么这绝对是一个怀味道, 应该让最邻近的对象来处理而不是跨越对象. 另一种情况是在连续调用之间传递相同类型的对象, 这样有利于消除临时变量并且带来流畅的操作.

要点是方法返回 self.