重构的方式和流程
- 两顶帽子:开发的时候,开发人员经常在两种状态间切换,或者带着重构的帽子重构代码,或者带着开发的帽子新增代码
- 小步前进:由于重构不改变现有代码的逻辑,所以对于一个大功能,可以先重构一部分,测试,再接着重构一部分
- 做好测试:重构都是修改现有代码,那么应当做到,以前能跑通的测试,重构后依旧可以跑通。这也是说,重构没有问题的一个表现是,通过所有预期的测试。
代码的坏味道(何时重构)
- 重复代码、异曲同工的类
- 过长函数:要写注释的地方,不妨分解一个函数,用函数名说明意图
- 过大的类
- 过长的参数列表:可以在函数内部通过调用其他函数得到的参数,可以不用通过入参得到
- 发散式变化、霰弹式修改:每次变化都要修改多个函数的时候,不妨将修改的地方提取到一处或者一个类中
- 依恋情节、狎昵关系:一个类对另一个类的数据和行为,比对自身更感兴趣,这时说明,类的功能划分有误
- 数据泥团:几项数据,通常一起出现,一起被需要,那么,他们可能需要结成对象
- 基本类型偏执:也许,使用小型对象,可以表示更多东西
- switch语句:switch语句有时意味着,可以使用面向对象的继承和多态来避免
- 面向未来编程、暂时性字段…:当前不用的字段和仅在一定情况下使用的字段,请谨慎编写
- 中间类:当一个类基本把所有操作全转接给其他类处理时,这个类的作用就有待商榷,也许可以去掉
- 被拒绝的遗赠:父类的全部方法,子类都应该继承,而不是选择一部分继承,如果是这样,可能父类需要把某些方法下沉
重构手法
重新组织函数
- 提炼函数/内联函数
- 引入解释性变量保存复杂表达式结果
- 将不同用途的临时变量区分开
- 使用函数对象取代函数:对于一个大型函数内,因为临时变量的缘故,函数无法拆解的情况,可以抽取函数对象,即保存变量,又有拆解的函数
对象之间搬移特性
- 搬移函数
- 搬移字段
- 提炼类/内联类
- 隐藏委托关系/移除中间类
- 引入外加函数/引入本地扩展(子类或者包装类)
重新组织数据
- 自封装字段:必要时再进行
- 以对象取代数据值
- 封装字段
- 封装集合(返回不可修改的集合)
- 以类取代类型码:当类型码不影响行为的时候,为了运行时检测安全,可以使用类取代类型码
- 以子类取代类型码:当类型码影响行为的时候,可以使用子类替代类型码,借助多态的优点,让与特定类型有关的代码下沉,这里对类型的取值函数,可以借用自封装字段
- 用状态模式/策略模式取代类型码:当类型码在对象存活期间会变化时
- 用类型码替代子类:当不同之处只在返回的类型码时
简化条件表达式
- 以卫语句取代嵌套条件表达式:就是将不合法的判断前置,提前return
- 以多态取代条件判断:首先,应当使用以子类/状态模式/策略模式替代类型码构建了继承体系,然后将条件判断的语句上移为抽象函数,由子类各自实现
- 引入null对象:在返回null的时候,考虑可否返回一个“空对象”,以避免反复的null校验
简化函数调用
- 移除参数:对于多态函数来说,复杂一些,需要考察是否每一个子类的实现都不需要这个参数
- 令函数携带参数/以明确函数名取代参数
- 以函数取代参数:函数入参可以通过调用其他参数即时获取,则无需入参
- 引入参数对象:为了抵抗前文提到的“数据泥团”
- 以工厂函数取代构造函数
- 以异常取代错误码
- 以测试取代异常
处理概括关系
- 字段上移/下移
- 函数上移/下移
- 构造函数上移
- 折叠继承体系:把子类融入父类
- 塑造模版函数:某些函数以相同顺序执行类似操作,将类似操作抽象相同签名,则某些函数的调用可以合并为一种,将其上移至超类
- 以继承取代委托/以委托取代继承:子类可能只需要父类的一部分功能,这时相对于继承,组合(委托)更为合适;反之,可以使用继承
个人感觉很有用的重构手法
- 使用函数对象取代函数
- 自封装字段
- 以子类取代类型码
- 引入null对象
- 以卫语句取代嵌套条件表达式
- 以异常取代错误码