第三章 代码的坏味道
从某种意义上讲,理解“何时应该重构”,“何时停止重构”比“知道如何重构”更重要!
作者在这一章里试图通过“代码的坏味道”这样一种暗喻来解释“重构的时机和场合”。任何人都没办法给出何时必须重构的精确衡量标准。理解并掌握这个标准需要我们的直觉。这也与项目的实际情况有关,例如质量要求,预期的服役年限,安全性要求等等。因此,我们必须培养自己的判断力,学会判断一个类内有多少实例变量才算太大,一个函数内有多少行代码才算太长。
1. Duplicated Code(重复代码)*****
代码重复是万恶之源。
如果两个毫不相关的类出现重复代码,你应该考虑对其中一个使用提炼函数,将重复代码提炼到一个独立的类中,然后在另一个类内使用这个新类。但是,重复代码所在的函数也可能的确只应该属于某个类,另一个类只应该调用它,抑或这个函数可能属于第三个类,而另两个类应该引用这第三个类。你必须决定这个函数放在哪儿更合适,并确保它被安置后就不会再在其它任何地方出现。这个时候,请忘却你“放哪儿都可以”的念头!
2. Long Method(过长的函数)**
一般情况下,函数只是太长并不会导致什么严重的问题。如果一个函数干着许多不相关联的任务,那么这是“职责不单一”的表现,我们就需要使用提炼函数重构它,让函数内的代码在同一个任务级别上相互协作。
如果函数职责单一,但是代码实在太长,那么不妨考虑用方法对象替换方法(Replace Method With Method Object)重构之。我们经常这样做,并且收到了不错的效果。
函数太长难以理解,其实你总有办法让它变得短小一些。
3. Large Class (过大的类)****
太多实例变量。分离之。
1)有时候类并非在所有时刻都使用所有实例变量。这时就应该提炼类或提炼子类重构之。
2)如果类中有太多代码,通常也就意味着代码重复。这时也可以用提炼类和提炼子类重构之。
3)如果Large Class类是个GUI类,那么可以把数据和行为分离到一个独立的类中。可以使用重复观察数据方法重构之。
4. Long Parameter List(过长的参数列)**
函数需要的东西多半可以在函数的宿主类中找到。再者,处理“过长的参数列”这种味道通常并不太困难。
5. Divergent Change(发散式变化)***
一个类受多种变化的影响。重构目标:满足单一职责原则。
6. Shotgun Surgery (霰弹式变化)***
一个变化会引发多个类的修改。
7. Feature Envy(依恋情结)****
函数对某个类的兴趣高过对自己所处类的兴趣。
8. Data Clumps(数据泥团)****
以类表示概念。
9. Primitive Obsession(基本类型偏执)****
以类表示概念。
10. Switch Statements(switch 惊悚现身)*****
多态是switch语句的天生杀手。
11. Parallel Inheritance Hierarchies(平行继承体系)****
另一种重复。让一个继承体系的实例引用另一个继承体系的实例。
12. Lazy Class类)****
冗余的类就如同冗余代码一样,必须果断的移除。
13.Speculative Generality(夸夸其谈未来型)***
不要告诉别人“万一我要用它呢?”。
14. Temporary Field(令人迷惑的临时字段)****
1)有时会看到这样一个对象:其内的某个实例变量仅为某种特定情况而设。在变量未被使用的情况下猜测其当初的设置目的,会让人发疯的。如果遇到这种情况,可以使用提炼类给这个可怜的孤儿创造一个新家,并把与其相关的所有变量和代码放进这个新家。
2)还有一种情况容易导致这种坏味道的出现:类中有一个复杂算法需要好几个变量,为了避免过长参数列,所以经常把这些变量做为类的成员字段。但实际上这些字段只在使用该算法时才有效。这时候就需要利用提炼类把这些变量和相关函数提炼到一个独立的类中。
在我的代码中,经常有这样的坏味道。
15. Message Chains(过度耦合的消息链)**
隐藏代理,增加中间人。
16. Middle Man(中间人)**
大部分情况下,我们认为委托好于继承,但也不全是。
17. Inappropriate Intimacy(狎昵关系)***
两个类过于亲密,花费太多时间去探究彼此的private部分。
18. Alternative Classes with Different Interfaces(异曲同工的类)***
提炼超类(基类)。
19. Incomplete Library Class(不完美的库类)
添加外加函数,引入本地扩展。这一招与其说是重构手法,不如说是编码技巧。
20. Data Class (幼稚的数据类)
一个类如果只有数据没有行为,起初他可能只是个孩子,但要通过重构帮助他长大----他应该承担他应该承担的责任。
21. Refused Bequest(被拒绝的遗赠)
通常,委托优先于继承。
22. Comments(过多的注释)
当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余。即我们应该尽可能撰写“自解释代码”。注释的用途有两个:一是可以用来记述将来的打算;二可以用来记录“为什么做某某事”。