1.DuplicatedCode
代码重复几乎是最常见的异味了。他也是Refactoring的主要目标之一。代码重复往往来自于copy-and-paste 的编程风格。 2.Longmethod 它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图放在一起。 3.LargeClass 大类就是你把太多的责任交给了一个类。这里的规则是OneClassOneResponsibility。 4.DivergentChange 一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。 5.ShotgunSurgery 这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。 6.FeatureEnvy 对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。 7.DataClumps 某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。 8.PrimitiveObsession 面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。 9.SwitchStatement 基于常量的开关语句是OO的大敌,你应当把他变为子类、state 或strategy。 10.ParallelInheritanceHierarchies 并行的继承层次是shotgunsurgery 的特殊情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。 11.LazyClass 一个干活不多的类。类的维护需要额外的开销,如果一个类承担了太少的责任,应当消除它。 12.SpeculativeGenerality 一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。 13.TemporaryField 一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。 14.MessageChain 消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。 15.MiddleMan 对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。 16.InappropriateIntimacy 某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。 17.AlternativeClasseswithDifferentInterfaces 做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。 18.IncompleteLibraryClass 要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。 19.DataClass 对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。 20.RefusedBequest 超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。
21.Comments 经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。
代码的坏味道 | 一般重构方法 | 使用模式重构 | 重复代码 | 提炼方法 提取类 方法上移 替换算法 链构造方法 | 构造Template Method 以Composite取代一/多之分 引入Null Object 用Adapter统一接口 用Fatory Method引入多态创建 | 过长方法 | 提取方法 组合方法 以查询取代临时变量 引入参数对象 保持对象完整 | 转移聚集操作到Vistor 以Strategy取代条件逻辑 以Command取代条件调度程序 转移聚集操作到Collecting Parameter | 过长参数列 | 以方法取代参数 引入参数对象 保持对象完整 | | 条件逻辑过度复杂 | 分解条件式 合并条件式 合并重复的条件片段 移除控制标记 以卫语句取代嵌套条件式 以多态取代条件式 引入断言 | 以Strategy取代条件逻辑 转移装饰功能到Decorator 以State取代状态改变条件语句 引入Null Object | 分支语句 | 提取方法 转移方法 以子类取代类型代码 以多态取代条件式 已明确方法取代参数 | 以State/Strategy取代类型代码 引入Null Object 以Command替换条件调度程序 转移聚集操作到Visitor | 基本类型迷恋 程序代码过于依赖基本类型(int,string,double,array等低层次语言要素) | 以对象取代数据值 以类型取代类型代码 以子类取代类型代码 提取类 引入参数对象 以对象取代数组 | 以State取代状态改变条件语句 以Strategy取代条件逻辑 以Composite取代隐含树 以Interpreter取代隐式语言 转移装饰功能到Decorator 用Builder封装Composite | 数据泥团 在类的字段和参数列中,总是一起出现的数据 | 提取类 引入参数对象 保持对象完整 | | 令人迷惑的临时字段 | 提取类 | 引入Null Object | 组合爆炸 许多段代码使用不同种类或数量的数据或对象做同样的事情(例如使用特定条件和数据库查询) | | 以Interpreter取代隐式语言 | 过大类 | 提取类 提取子类 提取接口 复制被监视数据 | 以Command取代条件调度程序 以State取代状态改变条件语句 以Interpreter取代隐式语言 | 冗赘类 不再做很多工作或没有用的类 | 折叠继承关系 内联Singleton | | 不恰当的暴露 在客户代码中不应看到类的字段和方法,却是公开可见的 | 封装字段 封装群集 移除设置方法 隐藏方法 | 用Factory封装类 | 发散式变化 类经常因为不同的原因在不同方向上发生变化,显然是违反了单一职责原则 | 提取类 | | 霰弹式修改 如果遇到变化,必须在不同的类中作出相应的修改 | 转移方法 转移字段 内联类 | 将创建知识搬移到Factory | 依恋情结 方法对于某个类的兴趣高过对自己所处的宿主类 | 转移方法 提取方法 | 引入Strategy 引入Visitor | 平行继承体系 当为一个类增加一个子类时,也必须在另一个类中增加一个相应的子类 | 转移方法 转移字段 | | 夸夸其谈未来性 | 折叠继承关系 内联类 移除参数 移除方法 | | 过度耦合的消息连 不断的向一个对象索求另一个对象 | 隐藏委托 提取方法 转移方法 | 使用抽象引入Chain Of Responsibility | 中间转手人 类接口中有很多方法都委托给其他类 | 移除中间转手人 内联方法 以继承取代委托 | | 狎昵关系 类之间彼此依赖于其private成员 | 转移方法 将双向关联改为单向 提取类 隐藏委托 以继承取代委托 | | 异曲同工的类 | 重命名方法 转移方法 提取超类 | 用Adapter统一接口 | 不完善的程序库类 | 引入外加方法 引入本地扩展 | 用Adapter统一接口 用Facade封装类 | 纯稚的数据类 只拥有字段的数据类 | 封装字段 封装集合 移除设置方法 转移方法 隐藏方法 | | 被拒绝的遗赠 继承父类时,子类想要选择继承的成员 | 以委托取代继承 | | 过多的注释 为糟糕的代码写大量的注释 | 使用一起重构方法,使方法本身达到自说明的效果,让注释显得多余 | | 怪异解决方案 在同一系统中使用不同的方式解决同一问题 | 替换算法 | 用Adapter统一接口 |
|