重构 -- 改善既有代码的设计
(1)重新组织函数
1 提炼函数(Extract method)
2 内联函数(inline method) 将某些函数去掉,代码提出
3 内联临时变量
4 以查询取代临时变量
5 引入解释性变量
6 分解临时变量
7 移除对参数的赋值
8 以函数对象取代函数 如果你有一个大便量,其中局部变量无法使用extra
(2)在对象之间搬移特性
1 搬移函数(Move method) 在该函数最常引用的类中建立一个有着类似行为的新函数。将就函数变成一个单独的委托函数,或是将旧函数完全移除。
2 搬移字段(Move field)如果一个类中的某个字段在另外一个类中更多的使用了
3 提炼类(Extract class) 建立一个新类,将相关的字段和函数从旧类搬移到新类
4 将类内联化(Inline class) 将这个类的所有特性搬移到另一个类中,然后移除原类。如果一个类不再承担足够责任。
5 隐藏委托关系(Hide delegate) 在服务类上建立客户所需的所有函数,用以隐藏委托关系。
6 移除中间人(Remove Middle Man) 同上反向
7 引入额外函数(Introduce Foreign method) 这个函数原本应该在提供服务的类中实现
8 引入本地扩展( Introduce local extension) 建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。
(3)重新组织数据
1 自封装字段(self encapsulate field) 为这个字段建立取值/设值函数,并且只以这些函数来访问字段。
2 以对象取代数据值 (replace data value with object) 数据项不再那么简单了,有了一些特殊行为。可以考虑将之放到一个类中。
3 将值对象改为引用对象(change value to reference) 如果你希望给这个对象加入一些可修改数据,并保证对任何一个对象修改都能影响到所有引用此对象的地方。
4 将引用对象改为值对象(change reference to value)
5 以对象取代数组( replace array with object ) 你有一个数组,其中元素各自代表不同的东西
6 复制“被监视数据”(Duplicate observed data) 可能需要使用不同的用户界面来表现相同的业务逻辑,与GUI分离后,领域对象的维护和演化更为容易。业务逻辑可以镶嵌于用户界面中,但是数据就需要复制到新的对象中,并提供同步机制。
7 将单向关联改为双向关联 在被控端建立一个辅助函数,其命名应该清楚指出它的有限用途。如果机油的修改函数在控制端,让它负责更新反向指针。如果既有的代码在被控端,就在控制端建立一个控制函数,并让修改函数调用这个新建的控制函数。
8 将双向关联改为单向关联 双向关联很容易造成该对象死亡了,却仍然保存在系统中,因为它的引用还没有完全清除。
9 以字面常量取代魔法数(Replace Magic number with symboli constant)创造一个常量,并根据其定义为它命名。
10 封装字段 数据生命为private,并提供访问接口
11 封装集合 为集合加入添加/删除函数。将保存的字段转化为空集合,返回集合不可修改内容。
12 以数据类取代记录 新建一个类,表示记录,并对数据项建立对应的private字段和提供设置取值函数。
13 以类取代类型码 建立一个新类替换该数值类型码
14 以子类取代类型码 如果类型码会影响宿主类的行为,那么最好的办法就是借助多态来处理变化行为。
15 以state/strategy取代类型码 有一个类型码,它影响类的行为但你无法通过继承手法消除它。 使用self encapsulate field将类型码自我封装起来,建立一个类根据类型码的用途为它命名,为这个类添加子类,每个子类对应一种类型码。在超累中建立一个抽象的查询函数,用以返回类型码,每个子类复写该函数并返回确切的类型码。在源类中建立一个字段,用以保存新建的状态对象。原类中建立一个字段,用以保存新建的状态对象。调整原类总负责查询状态类型码的函数,将查询动作转发给状态对象。调整原类中为类型码设值的函数,讲一个恰当的状态对象子类复制给“保存状态对象”的那个子段。
16 以字段取代子类 如果各子类的唯一差别只在“返回常量数据”的函数上,可以修改这些函数,使他们返回超类中的某个字段,然后销毁子类。
建立子类的目的是为了增加新特性或变化其行为。若子类中只有长两函数,实在没有足够的存在价值。
(4)简化条件表达式
1 分解条件表达式(decompose conditional) 从if、then、else三个段落中分别提炼出独立函数
2 合并条件表达式( consolidate conditional expression) 如果最终行为一致,可以将判断用与或讲条件合并
3 合并重复的条件片段( consolidate duplicate conditional fragments) 鉴别出执行方式补水条件变化而变化的代码,如果这些共同代码位于条件表达式尾端,就将它移到条件表达式之前。如果位于条件表达式尾端,可以到条件表达式之后。
4 移除控制标记 (remove control flag) 以break语句或return语句取代控制标记。 可以让条件语句更清晰。
5 以卫语句取代嵌套条件表达式( replace nested conditional with guard clauses) 判断中使用return提前返回,能使表达式清晰
6 以多态取代条件表达式(replace conditional with polymorphism) 将这个条件表达式的每个分支放进一个子类的复写函数中,然后将原始函数声明为抽象函数。
7 引入Null对象 (Introduce Null Object) 将null值替换null对象。
8 引入断言 (Introduce assertion) 某一段代码需要对程序状态做出某种假设。
(5) 简化函数调用
1 字段上移
2 函数上移
3 构造函数本体上移
4 函数下移
5 字段下移
6 提炼子类 类中的某些特性只被某些实例使用
7 提炼超类 为一些类建立超类,将相同特性移到超类中。
8 提炼接口( Extract Interface) 若干用户使用类接口中的相同子集,或者两个类的接口部分相同。
9 折叠继承体系(Collapse HIerarchy) 超类和子类无太大区别,将它们合为一体。
10 塑造模板函数(From TemPlate Method) 你有一些子类,其中相应的某些函数以某种相同次序执行类似的操作,但各个操作的细节有所不同。将会这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。
11 以委托取代继承 (Replace Inheritance with Delegation) 某个子类只使用超累接口中的一部分,或者根本不需要继承而来的数据。 在子类中新建一个字段以保存超类;调整子类函数,令它改而委托超类,然后去掉两者之间的继承关系。
12 以继承取代委托 (Replace Delegation with Ingeritance) 你再练个类之间使用委托关系,并经常为整个接口编写许多简单的委托函数。 还有其他选择:通过Remove Middle Man让客户自己调用受托函数,也可以使用Extract Superclass将两个类接口相同的部分提炼到超类中,然后让两个类都集成这个新的超类;你还可以使用类似的手法使用Extract Interfae。注意:当受托对象不只一个其他对象共享,而且受托对象是可变的。这时,就不能将委托关系替换为继承关系,因为这样就无法再共享数据了。
(6) 大型重构
1 梳理并分解集成体系 (Tease Apart Inheritance) 建立两个集成体系,并通过委托关系让其中一个调用另一个。
首先识别集成体系所承担的不同责任,然后建立一个二维表格,并以坐标轴表示出不同的任务。判断哪一项责任更重要一些,并准备将它留在当前集成体系中。准备将另一项这人移到另一个集成体系中。针对原集成体系中的每个子类,建立上述新类的一个子类。在原集成体系的子类中,将前一步骤所添加的实例变量初始化为新建子类的实例。
2 将过程化设计转化为对象设计 (Convert Procedural Design to Objects) 将数据记录变成对象,将大块的行为分成小块,并将行为一入相关对象之中。
3 将领域和表述/显示分离 它将用户界面代码和领域逻辑分离了。展现类中只含用以处理用户界面的逻辑;领域类不含任何与程序外观相关的代码,只含用以处理用户界面的逻辑。将这两块分离,程序未来的修改更加容易。 检查数据如果不属于UI和领域逻辑,就对他使用Duplicate Observed Data使它同时存在于两处,再运用Move Method将它移到领域类。
4 提炼集成体系 (Extract Hierarchy) 如果某个类做了太多的工作,其中一部分是以大量条件表达式完成的。建立集成体系,以一个子类表示一种特殊情况。
a. 新建一个子类,并对原始类试试Replace Constructor with Factory method,再修改工厂函数令它返回适当的子类实例。将含有条件逻辑的函数一个一个,注意复制到子类,然后简化这些函数。重复上述过程,将所有变化情况都分离出来,直到可以将超类声明为抽象类
b. 针对原始类的每一种变化情况,建立一个子类。使用Replace Constructor with factory method将原始类的构造函数转变成工厂函数并针对每一种情况变化返回适当的子类实例。针对含有条件逻辑的函数,实施Replace conditinal with polymorphism如果并非整个函数的行为有所变化,只是函数一部分有所变化,请先用extract method将变化部分和不便部分隔离开来。