《重构 改善既有代码的设计》读书笔记

《重构 改善既有代码的设计》读书笔记

Posted by ZW on November 15, 2018

是什么

  • 重构的定义 :再不改变代码外在行为的前提下,对代码做出修改,已改进程序内部结构

为什么

  • 改进软件设计
  • 使软件更容易理解
  • 帮助找到bug
  • 提高编程速度

什么时候做

  • 反对专门拨出时间进行重构
  • 事不过三,三则重构
  • 添加功能时重构
  • 改bug时重构
  • 复审代码时重构
  • 怎么对经理说?不要告诉经理
  • 如果项目接近最后期限,你也应该避免重构

哪些代码需要重构

  • 没有精确的标准,没有任何量度规矩比得上一个见识广博的者的直觉
  • 重复代码
  • 过长的函数
  • 需要注释来说明的代码块
  • 表达式和循环也常常是提炼的信号
  • 过大的类
  • 过长的参数列
  • 当修改一个地方造成其他多处影响(发散式变化)
  • 需要多处修改才能达到目的(霰弹式修改)
  • 类方法所用的最多的数据不在改类中(依恋情节)
  • 相同的数据项(数据泥团)
  • 过多使用基本数据类型,而没有使用结构化参数
  • switch
  • 平行继承
  • 冗赘的类
  • 没有用的函数,非必要的函数
  • 令人迷惑的临时字段
  • 过度耦合的消息链
  • 过度耦合的类
  • 做相同事情,不同参数的函数
  • 不完美的库类
  • 继承了超类不必要的属性
  • 过多的注释

怎么做

  • 重构前先检查自己是否有一套可靠的测试机制

  • 步子要小,不然容易扯到蛋

  • 保持测试,小修改,测试,小修改的节奏

  • 无论何时都要明白自己是在开发新功能还是重构

  • 在一个类方法中,不要在另一个对象的属性基础上运用switch语句

  • 除非有必要,否则不要发布接口

  • 添加特性之前,先写相应的测试代码

  • 测试你最担心出错的部分

  • 集中火力测试边界条件

  • 测试是否抛出了预期的异常

重构函数

  • 尽量使用细粒度的函数
  • 保证函数名称和函数本体之间的语意差距尽量小
  • 当函数本体和函数名同样通俗易懂时,就不要使用函数。(不适用于递归调用,多态,多点返回等场景)
  • 干掉只被一个简单复制的临时变量
  • 以查询取代临时变量
  • 引入解释性变量(将复杂的表达式赋值给一个变量)
  • 不要用同一个临时变量去表达不用的含义
  • 不要在函数内部对参数赋值
  • 以函数对象取代函数(把大型函数,局部变量成灾的函数抽象到另一个对象)
  • 当需要替换算法时,可以保留以前的算法,编写新的算法,用现有的测试新算法,于原本结果一致时在替换
  • 函数名应该要清晰的表达出函数的用处
  • 函数分为查询函数和修改函数,一定要区分一个函数是查询函数还是修改函数。必要时写第三个函数调用查询函数和修改函数
  • 一个函数的行为过多取决于参数时,应该分离成不同的函数
  • 若参数的参数是某个对象的若干值,则将这个对象作为参数
  • 当构造函数不仅仅做简单的构建动作时,以工厂函数取代构造函数
  • 用异常取代错误码返回
  • 预先检查条件

在对象之间搬移特性

  • 把函数和字段放在与它交流最多的类里
  • 如果一个类的职责开始不明确,那么就把它抽象成2个类。反之,如果一个类做的事情太少,可以将其特性移动到另一个类中
  • 隐藏委托关系
  • 如果某个类做了过多的简单委托动作,干掉他
  • 如果你需要扩张一个给你提供服务的类,又无法修改这个类时,可以新建一个函数,并以第一参数传入这个类的实例,或者使用子类,包装类。
  • 把子类共同的字段,函数,抽象到父类,反之,不相同的移到子类中。
  • 在子类行为相同时,塑造模板函数
  • 用委托取代继承

重新组织数据

  • 为字段建立取值、设值函数
  • 如果函数参数太多,可以用一个对象取代数据值
  • 如果一个数组中的元素代表不同的东西,用对象取代它
  • 不要魔法数,不要魔法数,不要魔法数!
  • 函数返回集合时,只返回一个可读的副本,并且提供添加/移除集合里元素的函数
  • 可以使用多态来取代类型码

简化条件表达式

  • 把if ,then,else 的条件和执行逻辑提炼成独立的函数
  • 如果一系列的条件,都得到相同的结果,可以合并这些条件到一个函数
  • 打破一个函数只有一个出口的原则
  • 条件表达式(如if)的两条分支都是正常行为则使用if…else,如果只有一种是正常行为,if():return
  • 用null对象解决代码中过多的非空判断
  • 断言很重要

未完待续…