“代码的坏味道”是来自Martin Flowler所著《重构 改善既有代码的设计》第三章的title,据作者说这一章来自Kent Beck。我觉得本章是应用重构的重点,因为它告诉我们在看到什么样的代码时就应该思考重构了,下面我就把22种坏味道以我的理解整理了一下。
-
重复代码(Duplicated Code):分为三种情况,一个类中含有相同的表达式,Extract Method(110),两个互为兄弟的子类中含有相同表达式,相同代码用Extract Method(110)和Pull Up Method(332),相似代码用Extract Method(110)将相似部分和差异部分分隔开,再使用From Template Method(345),选择使用Substitute Algorithm(139),两个毫不相关的类有相同表达式使用Extract Class(149)。
-
过长函数(Long Method):找到函数中适合集中在一起的部分,将它们提炼出来形成一个新函数,Extract Method(110),条件表达式和循环常常也是提炼的信号,Decompose Conditional(238)。如果提出的函数中有太多参数和临时变量,可以使用Replace Temp with Query(120)、Introduce Parameter Object(295)、Preserve Whole Object(288)、Replace Method with Method Object(135)。
-
过大的类(Large Class):这个没什么好说的,把一些彼此相关的成员变量和行为提出到新的类。Eetract Class(149)、Extract Subclass(330)、Extract Interface(341)。
-
过长参数列(Long Parameter List): 使用Replace Parameter with Method(292)、Preserve Whole Object(288)、Introduce Parameter Object(295)。
-
发散式变化(Divergent Change): 如果某个类经常因为不同的原因在不同的方向上发生变化,Divergent Change就出现了,eg:如果新加入一个数据库,必须去修改类中的三个函数。使用Extract Class(149)。
-
霰弹式修改(Shotgun Surgery): 如果遇到某种变化,都必须在不同的类中做出小修改。使用Move Method(142)和Move Field(146)、Inline Class(154)。
-
依恋情节(Feture Envy):函数对某个类的兴趣高过对自己所处类的兴趣。Move Method(142),Extract Method(110)。
-
数据泥团(Data Clumps):两个类中相同的字段、许多函数签名中相同的参数,这些总是绑在一起出现的数据应该拥有属于它们自己的类。Extract Class(149),Introduce Parameter Object(295)/Preserve Whole Object(288)。
-
基本类型偏执(Primitive Obsession):将表示money的数值和币种、range类的起始值由基本类型转换为小对象。Replace Data Value with Object,Replace Type Code with Class(218)、Replace Type Code with Subclass(213)、Replace Type Code with State/Strategy(227)。
-
switch惊悚献身(switch Statements): switch语句的问题在于重复。单一函数中的选择事例且不想改动它们,可以使用Replace Parameter with Explicit Methods(285)、Introduce Null Object(260)。如果不是单一函数中的选择事例可以考虑Replace Type Code with Subclass(213)、Replace Type Code with State/Strategy(227)。
-
平行继承体系(Parallel Inheritance Hierarchies): 平行继承体系是霰弹式修改的的特殊情况,在这种情况下每当为一个类增加子类时必须为另外一个类也增加子类。解决方法时让一个继承体系的实例引用另一个继承体系的实例,再使用Move Method(142)和Move Field(146)。
-
冗赘类(Lazy Class):重构后该类已经失去了意义可以不要了。使用Collapse Hierarchy(344),组件使用Inline Class(154)。
-
夸夸其谈未来性(Speculative Generality): 如果某个抽象类没有太大作用,运用Collapse Hierarchy(344),不必要的委托可运用Inline Class(154)除掉。如果函数的的某些参数没有使用上实施Remove Parameter(277),如果函数名称带有多余的抽象意味,应该实施Remove Method(273)让它具体一些。
-
令人迷惑的暂时字段(Temporary Field):类的某个实例变量仅为某种特定情况而设,使用Extract Class(149)给这个变量创建一个新对象,把和变量相关的行为搬过来。
-
过渡耦合的消息链(Message Chains):调用链过长导致对象关系发生变化时就需要去修改客户端代码。使用Hide Delegate(157)。
-
中间人(Middle Man):过渡运用委托 一般是某个类接口有一般的函数都委托给其它类。Remove Middle Man(160),如果这样不干实事的函数只有少数几个可以使用InlineMethod(117)把他们放到调用端,如果这些Middle Man还有其它行为可以运用 Replace Delegation with Inheritance(355)把它变成实责对象的子类。
-
狎昵关系(Inappropriate Intimacy): 两个类的关系过于紧密。使用Move Method(142)和Move Field(146),Change Bidirectional Association to Unidirectional(200)、Extract Class(149)、Hide Delegate(157)。继承造成的亲密关系可以使用Replace Inheritance with Delegation(352)。
-
异曲同工类(Alternative Classes with Different Interfaces): 如果两个类做同一件事有着不同的签名使用Remove Method(273)重新命名,并反复运用Move Method(142)直到两者协议一致。如果必须重复而赘余的移入代码才能完成这些可以运行Extract Superclass(336)来做。
-
不完美的类库(Incomplete Library Class): 如果只想修改库类的一两个函数可以运用Introduce Foreign Method(162),如果要添加一大堆行为就得运用Introduce Local Extension(162)。
-
纯稚的数据类(Data Class):类中拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。
-
被拒绝的馈赠(Refused):如果子类不想或不需要基础超类的函数和数据。新建兄弟类使用Push Down Method(328)和Push Down Field(329)推给兄弟。还可以使用Replace Inheritance with Delegation(352)。
-
过多的注释(Comments):过多的注释就意味着代码很乱,会有前面的各种坏味道之一。
具体重构列表可以阅读《重构 改善既有代码的设计》。
重新阅读后按照自己的理解分类如下:
1、按相同划分
- 重复代码(方法)
- 异曲同工的类
2、按过长划分
- 过大的类
- 过长的方法
- 过长的参数列表
3、按类职责划分不明
- 发散式变化
- 霰弹式变化
- 平行继承体系
- 依恋情节
- 狎昵关系
- 令人迷惑的暂时字段
- 被拒绝的馈赠
- 纯稚的数据类
4、按需新建类划分
- 数据泥团
- 基本类型偏执
5、按多余划分
- 冗赘类
- 夸夸其谈未来性
6、无法的单个划分
- switch惊悚现身
- 过度耦合的调用链
- 不完美的类库