现象:当某个 Class 因为外部条件的变化或者客户提出新的功能要求等时,每次修改要求我们更新 Class 中不同的方法。不过这种情况只有在事后才能觉察到,因为修改都是在事后发生的么(废话)。 重构策略:将每次因同一条件变化,而需要同时修改的若干方法通过 Extract Class 将它们提炼到一个新 Class 中。实现目标是:每次变化需要修改的方法都在单一的 Class 中,并且这个新的 Class 内所有的方法都应该与这个变化相关。 6 . Shotgun Surgery (霰弹式修改) 现象:当外部条件发生变化时,每次需要修改多个 Class 来适应这些变化,影响到很多地方。就像霰弹一样,发散到多个地方。 重构策略:使用 Move Method 和 Move Field 将 Class 中需要修改的方法及成员变量移植到同一个 Class 中。如果没有合适的 Class ,则创建一个新 Class 。实现目标是,将需要修改的地方集中到一个 Class 中进行处理。 比较 Divergent Change (发散式变化)和 Shotgun Surgery (霰弹式修改): 前者指一个 Class 受到多种外部变化的影响。而后者指一种变化需要影响到多个 Class 需要修改。都是需要修理的对象。 7 . Feature Envy (依恋情结) 现象: Class 中某些方法“身在曹营心在汉”,没有安心使用 Class 中的成员变量,而需要大量访问另外 Class 中的成员变量。这样就违反了对象技术的基本定义:将数据和操作行为(方法)包装在一起。 重构策略:使用 Move Method 将这些方法移动到对应的 Class 中,以化解其“相思之苦”,让其牵手。 8 . Data Clumps (数据泥团) 现象:指一些相同数据项目( Data Items ),如 Class 成员变量和方法中参数列表等,在多个 Class 中多次出现,并且这些数据项目有其内在的联系。 重构策略:通过使用 Introduce Parameter Object (创建新的参数对象取代这些参数)或 Preserve Whole Object (使用已存在的对象取代这些参数),实现使用对象代替 Class 成员变量和方法中参数列表,清除数据泥团,使代码简洁,也提高维护性和易读性。 9 . Primitive Obsession (基本型偏执狂) 现象:在 Class 中看到大量的基本型数据项目( Data Item ),如 Employee 类中有大量的数据成员, Employee#, FirstName, MiddleName, LastName, Address, State, City, Street, Zip, OfficePhone, CellPhone, Email…… 等等。 重构策略:使用 Extract Class (提炼新类)或 Preserve Whole Object (使用已存在的对象取代这些参数),实现使用对象代替基本型数据项目( Data Item )。如上述 Employee 类中就可分别提炼出 EmployeeName 和 EmployeeContact 两个新类。 10 . Switch Statements ( Switch 语句) 现象:同样的 Switch 语句出现在不同的方法或不同的 Class 中,这样当需要增加新的 CASE 分支或者修改 CASE 分支内语句时,就必须找到所有的地方,然后进行修改。这样,就比较麻烦了。 重构策略: (1) 首先采用 Extract Method 将 Switch 语句提炼到一个独立的函数。 (2) 然后以 Move Method 搬移到需要多态性( Polymorphism )的 Superclass 里面或者是构建一个新的 Superclass 。 (3) 进一步使用 Replace Type Code with Subclasses 或者 Replace Type Code with State/Strategy 。这步就比较麻烦些,不过记住如下基本规则:这里一般有 3 个 Class 分别为 Source Class 、 Superclass 和 Subclass 。 Source Class : l 使用 Self Encapsulate Field ,将 Type Code 成员变量封装起来,也就是建立对应的 Setter/Getter 函数。 l 在 Source Class 中增加一个 Superclass 类型的成员变量,用来存放 Subclass 实例对象。 l 在 Source Class 中的 Getter 函数,通过调用 Superclass 的 Abstract Query 函数来完成。 l 在 Source Class 中的 Setter 函数,通过调用 Superclass 中的 Static 工厂化方法来获取合适的 Subclass 实例对象。 Superclass : 新建的一个 Class (注:就是上面通过 Move Method 搬移生成的 Superclass ),根据 Type Code 的用途命名该 Class ,作为 Superclass 。 l 在 Superclass 中建立一个 Abstract Query 函数,用来获取 Subclass 的 Type Code 。 l 在 Superclass 中创建 Static 工厂化方法生产对应的 Subclass 对象,这里会存在一个 Switch 语句(不要再动脑筋来重构这个 Switch 语句了,这个 Switch 语句不会在多处重复存在,并且这里用于决定创建何种 Subclass 对象,这是完全可以接受的)。 Subclass : l 根据每一个 Switch/Type 分支,建立对应的 Subclass ,并且 Subclass 的命名可以参考 Switch/Type 分支的命名。 l 在每一个 Subclass 中重载 Superclass 的 Abstract Query 函数,返回特定的 Type Code 。 (4) 现在 Superclass 仍然存在 Switch 分支,是时候轮到 Replace Conditional with Polymorphism 上场了。具体而言,就是在每一个 Subclass 中创建重载方法(注:该方法是 Superclass 中含有 Switch 语句的方法),并将 Superclass 中 Switch 语句对应的 Case 分支剪切过来。最后将 Superclass 中该方法初象化 Abstract ,并清除 Switch 语句及其所有的 Case 分支。 这样就完成了整个重构过程,这个比较麻烦。 注:并不是一看到 Switch 语句及 CASE 分支,就马上 / 偏执狂采用上述重构策略进行重构,画蛇添足或吃亏不讨好(个人观点)。一般而言,只有看到多处出现相同的 Switch 语句时,才应该考虑进行重构。 11 . Parallel Inheritance Hierarchies (平行继承体系) 现象:为某个 class 增加一个 subclass 时,也必须为另一个 class 相应增加一个 subclass 。重构策略: 在一个 class 继承体系的对象中引用( refer to )另一个 class 继承体系的对象,然后运用 Move Method 和 Move Field 将被引用 class 中的一些方法和成员变量迁移宿主 class 中,消除被引用 class 的继承体系(注:这种平行继承体系好象比较少见也)。 12 . Lazy Class (冗赘类) 现象:某一些 class 由于种种原因,现在已经不再承担足够责任,有些多余了。如同国有企业冗余人员一样,需要下岗了。 重构策略:通过 Collapse Hierarchy ,将这些冗余的 class 合并到 superclass 或 subclass 中,或者通过 Inline Class (与 Extract Class 相反),将这些冗余 class 中的所有 Method/Field 迁移到其他相关的 class 中。 13 . Speculative Generality (夸夸其谈未来性) 现象:系统中出现一些无用的 abstract class ,或者非必要的 delegation (委托),或者多余的参数等等。 重构策略:分别使用 Collapse Hierarchy 合并 abstract class ,使用 Inline Class 移除非必要的 delegation ,使用 Remove Parameter 删除多余的参数。 14 . Temporary Field (令人迷惑的暂时值域) 现象: class 中存在一些 Field ,这些 Field 只在某种非常特定的情况下需要。 重构策略:通过 Extract Class 将这些孤独的 Field 及其相关的 Method 移植的一些新的 Class 中。提炼出来的新 Class 可能没有任何抽象意义,只是提供 Method 的调用,这些新 Class 一般称为 Method Object 。 15 . Message Chains (过度耦合的消息链) 现象:向一个对象请求另一个对象,然后再向后者请求另一个对象,……,这就是 Message Chain ,意味着 Message Chain 中任何改变,将导致 Client 端不得不修改。 重构策略:通过 Hide Delegate (隐藏委托关系)消除 Message Chain ,具体做法是在 Message Chain 的任何地方通过 Extract Method 建立一个简单委托( Delegation )函数,来减少耦合( Coupling )。 16 . Middle Man (中间转手人) 现象:过度运用 delegation ,某个 / 某些 Class 接口有一半的函数都委托给其他 class ,这样就是过度 delegation 。 重构策略:运用 Remove Middle Man ,移除简单的委托动作(也就是移除委托函数),让 client 直接调用 delegate 受托对象。和上面的 Hide Delegate (隐藏委托关系)刚好相反的过程。 由于系统在不断的变化和调整,因此 [ 合适的隐藏程度 ] 这个尺度也在相应的变化, Hide Delegate 和 Remove Middle Man 重构策略可以系统适应这种变化。 另外,可保留一部分委托关系( delegation ),同时也让 Client 也直接使用 delegate 受托对象。 17 . Inappropriate Intimacy (狎昵关系) 现象:两个 Class 过分亲密,彼此总是希望了解对方的 private 成分。 重构策略:可以采用 Move Method 和 Move Field 来帮助他们划清界限,减少他们之间亲密行为。或者运用 Change Bidirectional Association to Unidirectional ,将双向关联改为单向,降低 Class 之间过多的依存性( inter-dependencies )。或者通过 Extract Class 将两个 Class 之间的共同点移植到一个新的 Class 中。 18 . Alternative Classes with Different Interfaces (异曲同工的类) 现象:两个函数做相同的事情,却有不同的 signature 。 重构策略:使用 Rename Method ,根据他们的用途来重命名。另外,可以适当运用 Move Method 迁移某些行为,使 Classes 的接口保持一致。 19 . Incomplete Library Class (不完美的程序库类) 现象: Library Class (类库)设计不是很完美,我们需要添加额外的方法。 重构策略:如果可以修改 Library Class 的 Source Code ,直接修改最好。如果无法直接修改 Library Class ,并且只想修改 Library Class 内的一两个函数,可以采用 Introduce Foreign Method 策略:在 Client Class 中建立一个函数,以外加函数的方式来实现一项新功能(一般而言,以 server class 实例作为该函数的第一个参数)。 如果需要建立大量的额外函数,可应该采用 Introduce Local Extension :建立一个新 class ,使它包含额外函数,并且这个 class 或者继承或者 wrap (包装) source class 。 20 . Data Class (纯稚的数据类) 现象: Data Class 指:一些 Class 拥有 Fields ,以及用来访问 Fields 的 getter/setter 函数,但是没有其他的功能函数。(感觉这些 Data Class 如同 Entity Class 或 Parameter Class ,用来传递参数,我认为这种情况下没有必要重构。) 重构策略:找出其他 class 中访问 Data Class 中的 getter/setter 的函数,尝试以 Move Method 将这些函数移植到 Data Class 中,实现将数据和操作行为(方法)包装在一起,也让 Data Class 承担一定的责任(方法)。 21 . Refused Bequest (被拒绝的遗赠) 现象: Subclass 不想或不需要继承 superclass 的部分函数和 Field 。 重构策略:为 subclass 新建一个兄弟( sibling class ),再运用 Push Down Method 和 Push Down Field 将 superclass 中的相应函数和 Field 下推到兄弟 class ,这样 superclass 就只包含 subclass 共享的东西了。其实,也就是将 superclass 中一些与特定的函数和 Field 放到特定的 subclass 中, superclass 中仅包含 subclass 共享的函数和 Field 。 如果不想修改 superclass ,还可以运用 Replace Inheritance with Delegation 来达到目的。也就是以委托取代继承,在 subclass 中新建一个 Field 来保存 superclass 对象,去除 subclass 对 superclass 的继承关系,委托或调用 superclass 的方法来完成目的。 22 . Comments (过多的注释) 现象:(晕倒,这个也要重构, Remove 掉所有的 Comments 吗?不是。)当代码中出现一段长长的注释,一般是由于代码比较糟糕,需要进行重构,除去代码的坏味道。 重构策略:通过上面提及的各种重构策略,将代码的坏味道去除,使注释变成多余。 如果需要注释 / 解释一段代码做了什么,则可以试试 Extract Method ,提取出一个独立的函数,让函数名称解释该函数的用途 / 功能。另外,如果觉得需要注释来说明系统的某些假设条件, 也可尝试使用 Introduce Assertion (引入断言),来明确标明这些假设。 当你感觉需要撰写注释时,请先尝试重构,试着让所有的注释都变得多余。 5 . Divergent Change (发散式变化)