何时进行重构

重构可遵循三次法则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构,即 事不过三,三则重构。具体表现为:添加功能时重构、修补错误时重构、复审代码时重构。

对于现有代码根本无法正常运作、项目已近最后期限的场景不能进行重构,前者可以考虑拆分后重写,后者则是为了保证项目能正常上线。

具有哪些代码特征需要重构

2.1 Duplicated Code (重复代码)

如果你在一个以上的地点看到相同的程序结构,可以考虑设法将他们合而为一。

一般表现为:同一个类的两个函数含有相同的表达式;两个互为兄弟的子类内含有相同表达式;不同类中由于某种原因存在相同的或相似的表达式。

2.2 Long Method (过长函数)

如果一个函数超过了 50-80 行,你就可以考虑进行分解了。

一般从以下几点入手:分析临时变量和参数,考虑将注释部分封装到一个方法中,对于多个条件表达式和循环的部分进行封装。

2.3 Large Class (过大的类)

如果一个类有太多实例变量和函数,可以考虑将几个变量一起提炼到新类中,对于特征相似的函数可以考虑提炼到一个接口中,然后再针对不同接口分解这个类,拆分成多个实现类。

2.4 Long Parameter List (过长参数列)

如果函数的传参太多导致难以理解,且对于调用方造成了困扰,就应该考虑缩减了,最好函数的传参不要超过 10 个,如果太多可以考虑是否可以将传参封装到一个对象来进行传递。

2.5 Divergent Change (发散式变化) / Shotgun Surgery (霰弹式修改)

如果在修改程序某个功能时,需要修改多处地方,此时就应该考虑来重构它了,使该修改尽可能的集中在一个地方,便于后期维护。

2.6 Feature Envy (依恋情结)

如果某个类的函数为了计算某个值或其他行为需要从另一个对象那儿调用许多取值函数,此时就需要判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些数据摆在一起。

最根本的原则:将总是一起变化的东西放在一起,数据和引用这些数据的行为总是一起变化的,如果出现例外,我们就搬移那些行为,保持变化只在一地发生。

2.7 Data Clumps (数据泥团)

如果两个类中相同的字段、许多函数签名中相同的参数出现三四项,此时就需要将这些字段/参数删除缩短,减少字段和参数的个数,提炼到一个独立对象中。

2.8 Primitive Obsession (基本类型偏执)

所谓的基本类型偏执就是:对于对象和基本类型的取舍,如果多个基本类型字段总被放在一起使用的话,那么可以考虑将其定义为一个对象来进行使用了。

2.9 Switch Statement ( switch 惊悚现身)

少用 switch (或 case )语句,因为会出现许多重复,每当看到 switch 语句,如果条件语句很复杂,你就应该考虑使用多态来替换它。

2.10 Parallel Inheritance Hierarchies (平行继承体系)

如果你发现某个继承体系的类名称前缀和另一个集成体系的类名称前缀完全相同,即如果你为某个类增加一个子类,必须也为另一个类相应的增加一个子类,此时应该考虑重构了。消除这种重复性的一般策略是:让一个继承体系的实例引用另一个继承体系的实例。

2.11 Lazy Class (冗赘类) / Speculative Generality (夸夸其谈未来性)

如果一个类的所得不值其身价,它就应该消失。

如果一个类本应该做某事却试图以各式各样的钩子和特殊情况来处理一些非必要的事情,那么就应该考虑移除它;比如抽象类没有太多作用的话就除掉,如果函数的参数未用到就应该去掉,如果函数或类的唯一用户是测试用例,就应该去掉等。

2.12 Temporary Filed (令人迷惑的暂时字段)

如果某个实例变量仅为某种特定场景而定或者一个类中有个复杂算法,需要好几个变量,但是这些字段只在该算法时才有效,其他情况下只会让人迷惑,此时应该考虑将这些变量和其相关函数提炼到一个独立类中,提炼后的新对象将是一个函数对象。

2.13 Message Chains (过度耦合的消息链)

如果一个对象请求另一个对象,然后再向后者请求另一个对象,然后在请求另一个对象,这就是消息链。通常最好的选择是:先观察消息链最终得到的对象是用来干什么的,看看能够把使用该对象的代码提炼到一个独立函数中,把这个函数推入到消息链。