2020-09-01

Refresh 重构(Refactor)

阅读重构的摘要

最近在闲暇之余重(第)温(一..次)此书, 首先能感受到的, 无论你是新程序员还是老程序员, 这本书都已经不具备太多的可读性了.

由于本书成书年代久远, 那个时候软件行业还不够发达, 面向对象还没有被大数人理解, 加之编译器也非常落后, 设计模式也不深入人心, 所以文中提供的所谓重构的心法, 在当时或许有一些意义. 而今看来, 整书400多页的文字, 主要的思想就是「抽」, 无论是类, 接口, 方法, 逻辑, 还是参数. 但是为什么会有这么大的篇幅, 其中一半的内容是教你如何在一个IDE功能匮乏的年代, 以一个出错率更低的顺序, 来进行上面所说的多种抽象操作.

当然我去除了大量书中已经没有任何价值的点后, 总结了如下的一些内容, 应该大多数大家已经在工作实践中已有体会, 主要还是总结一下, 温故知新吧.

  • 思想方面
    • 如果发现加特性很难, 就需要重构
    • 重构前需要想想有没有可靠的测试机制
    • 重构要微小步伐, 容易发现错误
    • Extract method是最简单的重构
    • 写出机器认识的代码容易, 写出人类容易理解的才是应该的, 最大的影响就是命名
    • 做移动重构时, 最好先复制粘贴, 测后再删除旧的代码
    • 对于另外对象的switch往往可以通过继承, 多态来取代
    • 三次遇到不合理的, 可能就是重构的时候了
    • 重构往往通过加隔离层进行
    • 不要过早发布接口
    • 性能优化应该基于良好的代码结构, 性能瓶颈往往只会在很小的代码片段里
    • 每次遇到一个bug, 尽量写个单元测试覆盖
    • 测试集中覆盖边缘case
  • 代码坏味道与手段
    • 重复代码
      • 抽方法
    • 大函数
      • 抽方法, 抽类
    • 大类
      • 抽类, 抽子类
    • 太多参数
      • 参数变方法, 抽参数对象
    • 加新功能修改多个函数
      • 抽子类
    • 加新功能需要改很多类
      • 挪方法, 挪成员
    • 函数过度依赖其他类
      • 挪方法, 抽方法
    • 同样的几个参数到处出现
      • 抽参数对象
    • 大量同样的基本类型做参数
      • 抽类, 不同表达式可抽进子类
    • 很多switch
      • 考虑用多态取代
    • 子类需平行同时添加
      • 先持有对方, 再挪方法, 再挪变量, 再合并
    • 没人用的类
      • 做内部类
    • 没太大用处的抽象, 参数, 命名
      • 删除, 重命名, 内联
    • 临时变量命名模糊
      • 抽方法, 抽类
    • 消息链过长, 对象转换过多
      • 对同一对象的使用抽方法, 推入消息链消除对象
    • 过度代理
      • 内联代理
    • 注释太多
      • 可能代码写的太难懂, 需要重构
  • 数据
    • 临时变量被赋值多次, 则可以抽成方法
    • 有时候需要提取多个临时变量对逻辑进行解释, 当然更好的可以再抽成方法
    • Java按值传递, 本质上是对象的引用按值传递
    • 移除中间人与隐藏委托是相辅相成的
    • 如果类无法更改, 就写个函数包装他, 类似于工厂方法?
    • 如果不能修改, Adapter模式常常用于扩展方法功能
    • 子类修改某些变量的获取可以通过自封装, 把变量抽象成函数
    • 成员如果不用改变, 就由引用变为值对象, 即immutable对象
    • 当业务复杂度变高需要将类之间单向关系改为双向绑定关系, 甚至一对多与多对一的关系
    • 双向绑定可能会造成很多僵尸对象, 增加复杂度与空间占用, 所以只有真正需要的时候用, 删除可以通过将内部绑定查询转移为传参, 再在调用的位置进行判断是否为僵尸对象
    • 不要使用Magic Number, 常量即可以优化储存, 又可读性高
    • 封装字段可以控制字段的读写, 称之为数据隐藏
    • 对于返回集合的函数需要返回只读副本, 如果需要修改, 则可另行提供修改方法
    • 原始类型替换成类, 有类型字段的类转换为多个子类, 或者抽象成策略模式, 从而合理改变数据结构
    • 如果子类只有常量, 可以抽成变量放父类, 再添加工厂方法创建, 降低复杂度
  • 条件表达式
    • 条件表达式很复杂的时候, 可以将条件, 执行代码进行封装, 更表意
    • 嵌套很深的条件表达式的每个结果都作为返回值的话, 可以简化为多个if+return, 类似于swift里面的guard, 或者kotlin里面的?:return, 书里叫卫语句(Guard Clauses)
    • 嵌套很深的条件表达式可以将范围大的if反向, 实现提前return
    • switch表达式有时候可以通过多态来取代
  • 函数调用
    • 函数最好读写分离
    • 抽象重复性的函数用以复用
    • switch构造可以替换成工厂方法
    • 从对象内拿出参数再传递给某函数不如直接将对象传入函数
    • 当函数的参数来自另外一个函数时, 也可以删掉参数, 把函数调用挪进去
    • 函数参数过多可以同对象替代
    • 如果不需要设置就不要提供set方法, 变量都需要为final
    • 有时候用抛异常替代返回错误码, 如果程序无法继续进行的话
    • 如果可以条件逻辑避免Runtime异常, 应不要无脑try/catch
  • 继承关系
    • 子类同样的方法应该向上提, 并将不同的地方抽象为抽象方法分别继承
    • 子类构造复制父类字段应该通过父类构造, super
    • 超类与子类差不多, 就合并
    • 逻辑相同, 类型不同可以通过模板函数, 模板类解决
    • 不能滥用继承, 组合代理好过继承, 但也有特殊, 如果需要使用委托函数所有函数或者大量, 则就需要继承, 不过这一点在kotlin里面也可以通过by来代理
  • 复杂的重构
    • 整理复杂的继承关系, 该抽接口抽接口, 摘出更多深层子类
    • 领域与表述分离, 就是UI与逻辑分离
    • 过程抽象为对象
    • 提炼继承体系
Refresh 重构(Refactor)亚马逊站内推广 · 实战技巧20讲ad公司外贸好货交流会第4期亚马逊员工的血泪史:2018黑色星期五期间,数千名亚马逊员工因薪酬问题而罢工抗议!姜涵eClincher亚马逊如何从产品价格的角度进行采购成本控制?为什么公司严控广告ACOS不见得是明智的选择?

No comments:

Post a Comment