HOW

Tip

重构 不是 一件应该特别拨出时间做的事情,重构应该随时随地进行。

事不过三,三则重构

何时重构

  • 添加功能前重构
  • 复审代码时重构
  • 修补错误时重构

顺序

Danger

编写测试代码 -> 循环执行 { 小步重构 -> 测试 -> 提交到版本库 } -> 压缩提交

为原函数添加足够的结构,以便我能更好地理解它,看清它的逻辑结构 -> 封装

处理输入 -> 参数校验 -> 数据查询 -> 数据更新 -> [日志、异步] -> 处理输出

  • 编写测试代码
  • 首先,把变量声明移动到使用处之前。
  • 定义变量考虑用最准确简洁的名称,必要时带上数据类型
  • 将关联的代码放在一起,没有关联的代码之间用空行隔开
  • 上层代码调用方法,展示数据处理顺序,下层代码处理具体逻辑
  • 把大函数封装成小函数,组合调用(建议一个函数不超过1屏,强烈建议一个函数不超过2屏)
  • 多个函数使用相同参数,考虑封装数据处理类
  • 多个类使用相同函数,考虑抽象父类
  • 父类的函数被子类覆盖,考虑用委托调用取代父类函数
  • 先进行重构,然后再进行性能优化。

    我得先让代码结构变得清晰,才能做进一步优化;如果重构之后该循环确实成了性能的瓶颈,届时再把拆开的循环合到一起也很容易。

场景-策略

  • 当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余。
  • 如果你需要注释来解释一块代码做了什么,试试提炼函数
  • 如果函数已经提炼出来,但还是需要注释来解释其行为,试试用改变函数声明为它改名;
  • 如果你需要注释说明某些系统的需求规格,试试引入断言
  • 如果你不知道该做什么,这才是注释的良好运用时机(记述将来的打算之外,标记你并无十足把握的区域,为什么这么做)
  • 识别坏味道、测试先行、行为保持的变更动作,是重构的基本功
  • 重构的最佳时机就在添加新功能之前。
  • 当变量名表达不准确的时候修改变量名
  • 当变量可以通过其他变量计算得到时,用查询取代变量
  • 当需要手动写注释的时候,考虑封装函数
  • 当代码重复时,考虑封装函数
  • 当遍历过程做了多件事情时(如函数返回结果是两项关联很弱的数据),考虑拆分遍历
  • 当多个类使用同样的函数时,提炼函数到父类
  • 当父类的函数不被所有子类时,分类父类方法,使用委托调用取代继承
  • 当一组函数使用相同的变量组合时,考虑封装成类
  • 结对编程:在编程的过程中持续不断地进行代码复审。
  • 如何确定该提炼哪一段代码呢?一个很好的技巧是:寻找注释。
  • 条件表达式和循环常常也是提炼的信号
  • 使用类可以有效地缩短参数列表
  • 总是绑在一起出现的数据真应该拥有属于它们自己的对象
  • 继承常会造成密谋,因为子类对超类的了解总是超过后者的主观愿望。如果你觉得该让这个孩子独立生活了,请运用以委托取代子类或以委托取代超类让它离开继承体系。
  • 通常,如果类内的数个变量有着相同的前缀或后缀,这就意味着有机会把它们提炼到某个组件内。

    观察一个大类的使用者,经常能找到如何拆分类的线索。看看使用者是否只用到了这个类所有功能的一个子集,每个这样的子集都可能拆分成一个独立的类。

  • 过长的消息链 —— 使用隐藏委托关系

    通常更好的选择是:先观察消息链最终得到的对象是用来干什么的,看看能否以提炼函数把使用该对象的代码提炼到一个独立的函数中,再运用搬移函数把这个函数推入消息链

? 我本能的做法是先提炼成嵌套函数,然后再将其移入新的上下文。 但这种做法的麻烦在于处理局部变量,而这个困难无法提前发现,直到我开始最后的搬移时才突然暴露。 从这个角度考虑,即便可以先提炼成嵌套函数,或许也应该至少将目标函数放在源函数的同级,这样我就能立即看出提炼的范围是否合理。