第2章:重构
1. 重构(refactoring)就是在不改变外部行为的条件下对现有工作代码进行修改的过程。换言之,就是对如何做而不是做什么进行修改。重构的目的就是改善内部结构。
2. 重构在两个方面与TDD相关,其大部分工作是消除那些为使测试通过而引入的重复代码(duplication);还有就是,TDD提供了测试所构成的,可以让我们放手去重构的安全网。
3. 如果重复代码所完成的工作是有用的或重要的,我们要把代码抽取出来,然后再各个地点调用它。
4. 如果重复出现在继承层次中,那么我们或许能够将重复代码推送到更高一级的层次上。
5. 如果出现重复的是某些代码的结构而不是具体细节,那么将不同的部分抽取出来,并把公共结构部分制作成模版方法(template method)。
6. 编码时,向系统中添加新的功能但不进行重构;重构时,改变代码但不增加新的功能。
7. 意图不清晰的时候一般也需要重构,一种简单而强大的澄清意图的方法就是为事务挑选更好的名字。
8. 如果你看到一天注释或者觉得有必要编写一条注释的话,那么请首先要考虑重构或者重写代码。注释表明那些功能单位应该分别被抽取到独立的方法中去。
9. 如果有多个相关的操作数据的类,要考虑一些操作数据的方法合并到一个类中。
10. 避免让一个类对另一个类的内部细节知道的太多。要想处理这种情况,我们就需要对方法进行迁移,让那些彼此了解的代码处于同一个位置。比如,不要过多读取一个类的信息,而要让那个类本身去操作自己。
11. 继承也会让子类过多地知道父类的细节,需要把祖先的一些细节私有化(去藕,decouple),或者把继承改为委派(delegation)。
12. 避免一个类的尺寸过大,因为可能它要完成的工作太多,可能了解的东西太多了,可能条件判断(condition)太多了?如果这样的话,也许可以提取出子类并且采用多态(polymorphism)来看看代码是否能够恰当执行。如果有明确清晰的子功能集合,那么或许能够把它们提出出来形成自己的类。尺寸过大有如下特征:运行代码行(LOC)度量工具;查看其UML图。
13. 避免懒惰类,这个类于大尺寸类的情况刚好相反,没有体现出类的价值。
14. 避免方法太长,需要将功能内聚的代码快拆分到各自独立的方法中。
15. 不加约束地使用switch语句(case语句)意味着没有深入理解面向对象的原则。一般可以使用多态出现一种更好的、更整洁的方式达到相同的目的。
16. 避免散弹式的外科手术(Shotgun Surgery),当我们改动一处代码时,发现需要改动多个地方。一般可以通过采用“用多态代替条件判断”的方法重构代码。
17. 重构前将自动测试准备停当,以避免重构改变代码的外部行为(behavior);一小步一小步地进行重构,一发生错误马上就能知道。
18. 有时候,使用图形化的角度观察事物看待系统能更容易发现问题。
19. 重构方法“提取类(Extract Class)”:提取多块同组的内聚行为一个新类。如果需要某种行为的多种实现,那么可以从类中抽取接口(interface)。
20. 重构方法“提取接口(Extract Interface)”:或许想要对具体的实现进行抽象,以便更容易地使用一种称为模拟对象(Mock Objects)的技术,通过接口来定义系统中的各组重要行为往往是很方便的。
21. 重构方法“提取方法(Extract Method)”:当一个方法太长或者逻辑过于复杂而不易理解时,我们可以将其中的某些部分提取出来而形成各自独立的方法。
22. 重构方法“用子类来代替类型代码(Replace Type Code with Subclasses)”:如果要使用类型代码来表示子类型的话,应该使用这种重构方法,为每种类型分别设计子类(解决switch乱来的问题)。
23. 重构方法“用多态来代替判断(Replace Conditional with Polymorphism)”:和上一条的目的和解决方法比较相似。
24. 重构方法“形成模版方法(Form Template Method)”:如果多个类中都有某种具有相同结构但不同细节的相似方法时就可以使用这种重构方法。最终要把这个具有相同结构的方法安置在超类中,并把提取出来的具体方法放在子类中,多态会负责调用那个函数的。
25. 重构方法“引入解释变量(Introduce Explaining Variable)”:当表达式复杂而且难以理解时,我们就可以提取其中的某些部分,把中间结果保留在清楚的临时变量中。
26. 重构方法“使用工厂方法代替构造方法(Replace Constructor with Factory Method)”:为了编码的清晰,可以将构造函数私有化,然后是使用工厂来产生实例。
27. 重构方法:“使用委托代替继承(Replace Inheritance with Delegation)”:如果只是为了重用超类的部分功能,就应该使用委托,把“超类”作为一个实例变量,以此来保存对象。
28. 重构方法:“使用符号常量来替代魔幻数字(Replace Magic Number with Symbolic Constant)”:这更具一般性,适用于字符串在内的任何数值。
29. 不能简单地将设计模式进行拼接(Composites),要把设计模式作为重构的目标;不应该不加变通地套用设计模式,而是通过重构渐渐引入设计模式,而且只在必要的时候。
30. 除了对开发代码外,对测试代码也要进行重构。