3.1 Duplicated Code(重复代码)
坏味道行列中首当其冲的就是Duplicated Code。如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更换。
最单纯的重复代码就是“同一个类的两个函数含有相同的表达式”。另一种常见情况就是“两个互为兄弟的子类含有相同表达式”。如果两个毫不相关的类出现重复代码,应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立类中,然后在另一个类内使用这个新类。
3.2 Long Method(过长函数)
百分之九十九的场合里,要把函数变小,只需使用Extract Method。找到函数中适合集中在一起的部分,将它们提炼出来形成一个新函数。
3.3 Large Class(过大的类)
3.4 Long Parameter List(过长参数列)
3.5 Divergent Change(发散式变化)
我们希望软件能够容易被修改。一旦需要修改,我们希望能够跳到系统的某一点,只在该处做修改。
如果某个类经常因为不同原因在不同的方向上发生变化,Divergent Change就出现了。你应该找出某特定原因造成的所有变化,然后运用Extract Class将它们提炼到另一个类中。
3.6 Shotgun Surgery(散弹式修改)
Divergent Change是指“一个类受多种变化的影响”,Shotgun Surgery则是指“一个变化引发多个类相应修改”。这两种情况下你都会希望整理代码,使“外界变化”与“需要修改的类”趋于一一对应。
3.7 Feature Envy(依恋情结)
对象技术的全部要点在于:这是一种“将数据和对数据的操作行为包装在一起”的技术。有一种经典气味是:函数对某个类的兴趣高过对自己所处类的兴趣。无数次经验里,我们看到某个函数为了计算某个值,从另一个对象那儿调用几乎半打的取值函数。疗法显而易见:把这个函数移至另一个地点。
3.8 Data Clumps(数据泥团)
常常可以在很多地方看到相同的三四项数据:两个类中相同的字段、许多函数签名中相同的参数。这些总是绑在一起出现的数据真应该拥有属于它们自己的对象。首先请找出这些数据以字段形式出现的地方,运用Extract Class将它们提炼到一个对象中。然后将注意力转移到函数签名上,运用Introduce Parameter Object 或Preserve Whole Object为它减肥。
3.9 Primitive Obsession(基本类型偏执)
大多数编程环境都有两种数据:结构类型运行你将数据组织成有意义的形式;基本类型则是构成结构类型的积木块。结构总是带来一定的额外开销。它们可能戴表着数据库中的表,如果只为做一两件事儿创建结构类型也可能显得太麻烦。
3.10 Switch Statements(switch惊悚现身)
面向对象程序的一个最明显特征就是:少用switch(或case)语句。从本质上说,switch语句的问题在于重复。你常会发现同样的switch语句散步于不同地点。如果要为它添加一个新的case子句,就必须找到所有switch语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。
大多数时候,一看到switch语句,就应该考虑以多态来替换它。问题是多态该出现在哪儿。
如果只是在单一函数中有些选择事例,且并不想改动它们,那么多态就有点杀鸡用牛刀了。这种情况下Replace Parameter with Explicit Methods是个不错的选择。如果你的选择条件之一是null,可以试试Introduce Null Object。
3.11 Parallel Inheritance Hierarchies(平行继承体系)
它是Shotgun Surgery的特殊情况。在这种情况下,每当腻味某个类增加一个子类,必须也为另一个类相应增加一个子类。如果你发现某个继承体系的类名称前缀和另一个继承体系的类名称前缀完全相同,便是这种坏味道。
消除这种重复性的一般策略是:让一个继承体系的实例引用另一个继承提下的实例。如果再接再厉运用Move Method和Move Field,就可以将引用端的继承体系消弭于无形。
3.12 Lazy Class(冗缀类)
如果一个类的所得不值其身价,它就应该消失。
3.13 Speculative Generality(夸夸其谈未来性)
如果某个抽象类其实没有太大作用,请运用Collapse Hierarchy。不必要的委托可运用Inline Class除掉。如果函数的某些参数未被用上,可对它实施Remove Parameter。如果函数名称带有多余的抽象意味,应该对它实施Rename Method,让它现实一些。
3.14 Temporary Field(令人迷惑的暂时字段)
某个实例变量仅为某种特定情况而设,这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。
3.15 Message Chains(过渡耦合的消息链)
如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象。。。这就是消息链。实际代码中你看到的可能是一长串getThis()或以长串临时变量。采取这种方式,意味客户代码将于查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。
这时候你应该使用Hide Delegate。你可以在消息链的不同位置进行这种重构手法。
3.16 Middle Man(中间人)
对象的基本特征之一就是封装--对外部世界隐藏其内部细节。封装往往伴随着委托。
但是容易过度使用委托。你也许会看到某个类有一半的函数都委托给其他类,这就是过度委托。这时应该使用Remove Middle Man,直接和负责的对象打交道。
3.17 Inappropriate Intimacy(狎昵关系)
两个类过于亲密。
继承往往造成过度亲密,因为子类对超类的了解超过后者的主观愿望。
3.18 Alternative Class with Difference Interface (异曲同工的类)
如果两个函数做同一件事,却有着不一样的签名,请运用Rename Method根据他们的用途重新命名。
3.19 Incomplete Library Class(不完美的库类)
复用常被视为对象的终极目的。但是也经常被高估--大多数对象只要够用就好。
3.20 Data Class(纯稚的数据类)
3.21 Refused Bequest(被拒绝的遗赠)
如果子类复用了超类的行为(实现),却又不愿意支持超类的接口,Refused Bequest的坏味道就会变得浓烈。可运用Replace Inheritance with Delegation来达到目的。
3.22 Comments(过多的注释)