1 重构概述
重构是测试驱动中的一个主要阶段。测试驱动主要分为三个阶段,第一阶段,书写测试用例,使用用例的方式来描述模块应该具有的功能。第二阶段,实现功能,让所有的测试用例通过,这阶段的工作,主要是从功能上,而不是从结构上对代码进行实现,因此,这部分的代码必然是存在一定的结构缺陷,例如结构不清,代码重复等。第三阶段,重构已有代码,重构的目标就是对第二阶段的功能代码的结构缺陷进行处理,但是重构不能对代码的功能进行改变,因此,当进行了重构以后,必须重新运行测试用例,以保证功能的完整。
所以,总的来说,测试驱动中,测试用例保证了程序的正确性,功能实现保证了程序的完整性,和重构则保证了程序的结构合理性。
2 重构步骤
由于重构本身是调整已有代码的结构,因此,理论上并不会修改程序的功能,但是,我们需要有一些手段或者措施来保证程序在经过重构以后的确没有发生功能性改变。而我们能使用的工具则是测试驱动第一阶段的产物――测试用例。
因此,对于所有的重构方法,我们都是先做少量的修改,然后运行测试用例,然后继续修改,如果哪一步测试用例不能通过,则表示最近一次的重构是有问题的,那么我们应该能回滚到上一次测试成功的状态,重新重构。
3 重构时机
3.1 说明:当出现以下情况时,表示代码存在着问题,应当考虑重构。
3.2 分类:
名称 | 描述 | 常用重构手法 |
Duplicated Code | 指两个方法具有相同的程序结构 | 4.1,5.3,9.2,9.10 |
Long Method | 指方法体过长 | 4.1,4.4,4.8,7.1 |
Large Class | 指一个类由于承担了太多的责任,而变得非常庞大 | 5.3,9.6,9.8,6.2 |
Long Parameter List | 指参数列过长 | 8.8,8.9,8.7 |
Divergent Change | 指类的一个变化,导致类中多个方法的修改 | 5.3 |
Shotgun Surgery | 指系统的一个变化,导致多个类中的方法的修改 | 5.1,5.2,5.4 |
Feature Envy | 指类中一个方法的实现,主要依赖了另一个类中的方法或字段 | 5.1,5.2,4.1 |
Data Clumps | 指某些字段总是联合在一起作为某些方法的参数,但是这些字段却散布在多个不同的类当中 | 5.3,8.9,8.7 |
Primitive Obsession | 指热衷于使用基本类型,而不使用类类型 | 6.2,5.3,8.9,6.5,6.13,6.14,6.15 |
Switch Statements | 指使用了switch结构 | 7.6,6.14,6.15,8.6,7.7 |
Parallel Inheritance Hierarchies | 指两个继承体系形成了平行结构,即当为A体系中的某个类新增一个子类时,需要在B体系中的对应类中新增一个对应的子类 | 5.1,5.2 |
Lazy Class | 指类不承担任何功能 | 5.4,9.9 |
Speculative Generality | 指过度强调将来的变化,而导致继承体系过度泛化 | 9.9,5.4,8.3,8.1 |
Temporary Field | 指类中的某个方法需要用到一些临时变量,但这些变量却作为类的属性存在 | 5.3,7.7 |
Message Chains | 指过度耦合的消息链,即A方法的执行,需要依赖B,B又要依赖C | 5.5 |
Middle Men | 指一个类的主要工作是在两个类之间进行请求转发 | 5.6,4.2,9.12 |
Inappropriate Intimacy | 指两个类间过度耦合 | 5.1,5.2,6.8,9.11,5.5 |
Alternative Class with Difference Interfaces | 指两个函数实现了相同的功能,但具有不同的声明 | 8.1,5.1 |
Incomplete Library Class | 指我们所依赖的类库存在着缺陷 | 5.7,5.8 |
Data Class | 指一个类只声明了由于获取和设置字段的方法,其他什么功能性方法都没有 | 5.1,6.10,6.11 |
Refused Bequest | 指一个类继承了他所不希望的父类特性 | 9.11 |
Comments | 指一个方法的注释过多,这表明了该方法完成了太多的事情 | 4.1,7.8 |