1. Bad Smell1
    1. Duplicated Code
      1. Case1: in different methods of same class
        1. Extract Method
      2. Case2: in brother class
        1. Extract Method, Pull Up Method
      3. Case3: in brother classes, same in algorithm structure
        1. Extract Method, Form Template Method
      4. Case 4: in different classes
        1. Extract Class, but consider it serious, since it will bring coupling
    2. Long Method
      1. Smell
        1. 关键不在于函数多长,而在于函数“做什么”和“如何做”之间的语义距离
        2. 每当感觉需要点注释的时候,就是提取方法的时候
      2. Case1: 99% cases
        1. Extract Method
      3. Case2: too many temps and parameters after extract methods
        1. Replace Temp with Query, Introduce Parameter Object and Preserve Whole Object
      4. Case3: still too many temps and parameters after done above
        1. Extract Method Object
      5. Case4: conditions
        1. Decompose Conditional
      6. Case5: loops
        1. Extract Loops to Method
    3. Large Class
      1. Smell
        1. 类并非在所有时刻都使用所有实例变量
      2. Case1: 提炼新的组件
        1. Extract Class
      3. Case2: 新的组件适合做子类
        1. Extract Subclass
      4. Case3: 运用Extract Interface为每一种客户端的使用方式抽取接口,可以帮助看清楚如何分解这个类
    4. Long Parameter List
      1. Case1: 如果向一个已有的对象发一个请求就可以取代一个参数
        1. Replace Parameter with Method
      2. Case2: 来自同一个对象的一大堆数据
        1. Preserve Whole Object
      3. Case3: 某些数据缺乏合理的对象归属
        1. Introduce Parameter Object
      4. 例外: 引入对象其实会造成依赖关系,这时候可以将数据从对象拆解出来单独作为参数。两种方式权衡利弊。
    5. Divergent Change
      1. Smell
        1. 一个类受多种变化影响
        2. 一个类只应为一种外界原因而变化
      2. 找出某特定原因造成的所有变化
        1. Extract Class
    6. Shotgun Surgery
      1. Smell
        1. 一个变化影响多个类
        2. 跟Divergent Change刚好相反。他们的最终的目标都是让一个变化对应一个类。
      2. 把所有需要修改的代码放进同一个类中
        1. Move Method, Move Field
        2. Inline Class (Extract Class相反)
    7. Feature Envy
      1. Smell
        1. 函数或函数的一部分对某个类的兴趣,高过对自己类的兴趣
        2. 数据和引用这些数据的行为应该一起变化
      2. Case1: 函数
        1. Move Method
      3. Case2: 函数的一部分
        1. Extract Method, Move Method
      4. 例外:Strategy和Visitor模式将少量需要被覆写的行为隔离开来,使得你可以轻松修改函数行为。当然也付出了“多一层间接性”的代价
  2. Bad Smell2
    1. Data Clumps
      1. Smell
        1. 总是绑在一起出现的数据
        2. 删掉众多数据中的一项,如果其他数据因而失去意义,就是个明确信号:他们应该产生一个新对象。
      2. Case1:以字段形式出现
        1. Extract Class
      3. Case2:以参数形式出现
        1. Introduce Parameter Object或者Preserve Whole Object
      4. 一旦拥有新对象,就可以寻找Feature Envy
    2. Primitive Obsession
      1. Smell
        1. 大量使用基本类型
        2. 走出传统的洞窟,进入对象世界
      2. Case1:将原本单独存在的数据替换为对象
        1. Replace Data Value with Object
      3. Case2;想要替换的数据是类型码,而他并不影响行为
        1. Replace Type Code with Class
      4. Case3:有与类型码相关的条件表达式 (类型码决定不同行为),宿主类可以被继承
        1. Replace Type Code with Subclass
      5. Case4:类型码的值在对象生命周期中变化 或 其他原因使得宿主类不能被继承
        1. Replace Type Code with State/Strategy(创造另一个继承体系)
      6. Case5:在数组中挑选数据
        1. Replace Array with Object
    3. Switch Statements
      1. Smell
        1. 同样的switch语句散布在不同地点。一旦修改,就需要修改所有地方
        2. 一看到switch,就考虑是否可以以多态替代它
      2. Case1:switch根据类型码进行选择
        1. Replace Type Code with Subclasses 或 Replace Type Code with State/Strategy
        2. 然后Replace Conditional with Polymorphism
      3. Case2:只在单一函数中有些选择事例,那么多态有点杀鸡用牛刀了。
        1. Replace Parameter with Explicit Methods
      4. Case3:如果选择条件之一是null
        1. Introduce Null Object
    4. Parallel Inheritance Hierarchies
      1. Smell
        1. Shotgun Surgery的特殊情况
        2. 每当为某个类增加一个子类,必须也为另一个类相应增加一个子类
      2. 一般策略是:让一个继承体系的实例引用另一个继承体系的实例
      3. 再运用Move Method和Move Field,将引用端的继承体系消弭于无形
    5. Lazy Class
      1. Smell
        1. 重构使得某个类身形缩水
        2. 事前添加了一个类来应付某些变化,但实际上并没有发生
      2. Case1:如果某些子类没有做足够的工作
        1. Collapse Hierarchy
      3. Case2:几乎没用的组件
        1. Inline Class
    6. Speculative Generality
      1. Smell
        1. "我想我们总有一天需要做这事"
        2. 以各种各样的钩子和特殊情况来处理一些非必要的事情,造成系统更难理解和维护
      2. Case1:某个抽象类其实没有太大作用
        1. Collapse Hierarchy
      3. Case2:不必要的委托
        1. Inline Class
      4. Case3:函数的某些参数未被用上
        1. Remove Parameter
      5. Case4:函数名称带有多余的抽象意味
        1. Rename Method
      6. Case5:函数或类的唯一用户是测试用例
        1. 删除代码和测试代码
    7. Temporary Field
      1. Smell
        1. 对象某些实例变量仅为某种特定情况而设,大多数情况下不需要用到
      2. Case1:类中有个复杂算法,需要好几个变量。由于实现者不希望传递一长串参数,所以把这些参数都放进字段中
        1. Extract Class提炼成函数对象
  3. Bad Smell3
    1. Message Chain
      1. Smell
        1. 用户向一个对象请求另一个对象,然后再向后者请求另一个对象。。。
        2. 实际代码中你看到的可能是一长串get方法或临时变量
        3. 客户代码将与查找过程中导航结构紧密耦合,一旦对象间的关系发生任何变化,客户端就不得不作出相应修改
      2. Case1:往往会
        1. 在消息链的不同位置使用Hide Delegate,但往往会把一系列对象变成Middle Man
      3. Case2:更好的选择
        1. 观察消息链最终得到的对象是用来干什么的,以Extract Method把这段代码提炼到一个独立函数,再使用Move Method把这个函数推入消息链
      4. 并不是任何函数链都是坏东西
    2. Middle Man
      1. Smell
        1. 过度使用委托
      2. Case1:过度运用委托
        1. Remove Middle Man (与Hide Delegate相反),直接和真正负责的对象打交道
      3. Case2:只有少数几个函数“不干实事”
        1. Inline Method
      4. Case3:为受托类整个接口编写简单的委托函数,而自己又有一些额外的方法
        1. Replace Delegation with Inheritance
    3. Inappropriate Intimacy
      1. Smell
        1. 两个类过分亲密,花费太多时间探究彼此的private元素
      2. Case1:两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性
        1. Change Bidirectional Association to Unidirectional
      3. Case2:如果两个类确实彼此需要
        1. Extract Class把两者共同点提炼出来,让他们使用这个新类
      4. Case3:继承往往造成过度亲密,子类对超类的了解总是超过后者的主观意愿。如果你觉得该让这个孩子独立省后。
        1. Replace Inheritance with Delegation
    4. Alternatives Classes with Different Interfaces
      1. Smell
        1. 异曲同工的类
      2. Case1:两个函数做同一件事情,却有着不同的签名
        1. Rename Method
      3. Case2
        1. Extract Superclass
    5. Incomplete Library Class
      1. Smell
        1. 库往往构造的不够好,而且往往不可能让我们修改其中的类
      2. Case1:需要为提供服务的类增加一个函数
        1. Introduce Foreign Method
      3. Case2:想要添加一大堆额外行为,或者有许多类需要同样的外加函数
        1. Introduce Local Extension
    6. Data Class
      1. Smell
        1. Data Class:拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物
        2. Data Class可以作为一个起点,但它必须承担一定责任
      2. Case1:public field
        1. Encapsulate Field
      3. Case2:包含容器类字段,避免它在宿主对象完全不知情的情况下被人修改
        1. Encapsulate Collection
      4. Case3:不该被其他类修改的字段
        1. Remove Setting Method
      5. 然后找出取、设值函数使用的地点,使用extract method和move method把相关行为移到Data Class。之后可以运用hide method隐藏取值设值函数
    7. Refused Bequest
      1. Smell
        1. 子类不想或不需要继承超类的函数和数据
        2. 如果Refused Bequest引起困惑和问题,请遵循传统忠告:所有超类都应该是抽象的。但不必每次都这么做,因为它的坏味道很淡。
      2. Case1:子类复用了超类的行为,却又不愿意支持超类的接口。Refused Bequest坏味道就变得很浓烈。
        1. Replace Inheritance with Delegation
    8. Comments
      1. Smell
        1. 一段代码有长长的注释,这些注释之所以存在,是因为代码很糟糕
      2. Case1:需要注释来解释一段代码做了什么
        1. Extract Method
      3. Case2:函数已经提炼,但还是需要注释解释其行为
        1. Rename Method
      4. Case3:如果需要注释说明某些系统的需求规格
        1. Introduce Assertion
      5. 用各种方法重构糟糕的代码,最后会发现注释已经变得多余。当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余。
  4. 重构原则
    1. 重构定义
      1. 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本
    2. 为何重构
      1. 改进软件设计
      2. 更容易理解
      3. 帮助找到bug
      4. 提高编程速度
    3. 重构和设计
      1. 重构带来更简单的设计,同时又不损失灵活性
      2. 降低了设计过程的难度,减轻了设计压力。不必再预先思考过多的灵活方案
    4. 重构和性能
      1. 构造良好的代码可以让你更加快速添加功能,也就有了更多时间优化性能
      2. 面对构造良好的程序,性能分析便有了较细的粒度,度量工具可以把你带入范围较小的程序段落中,性能调整也变得容易。
  5. 构筑测试体系
    1. 测试是重构的防护墙