1. 重新组织函数
重构手段 | 动机 | 做法 |
---|---|---|
Extract Method(110) 提炼函数 | 1.函数粒度小更容易复用 2.高层函数就像一系类注释 3.函数粒度细,函数覆盖更容易 | |
Inline Method(117) 内联函数 | 1.一个和Extract Method相反的用法,每个函数其内部代码和函数名名称一样清晰易读 2.如果小函数划分混乱的时候,也可以应该方法先合并为大函数,再拆分小函数。记住,并不是所有间接层都有价值 | 1.观察是否有多态 2.找出这个函数的所有被调用点 3.找出这个函数的所有被调用点都替换为函数本体 |
Inline Temp(119) 内联临时变量 | 1.Resplace Temp with Query 的一部分 2.发现某个临时变量被赋予某个函数的返回值 | 1.最好先将该变量声明为final,然后编译,确保临时变量只被赋之过一次值 |
Replace Temp whith Query(120) 以查询替代临时变量 | 临时变量只能在所属函数内使用,它会处使你写更长的函数 | 1.找出赋值一次的临时变量 2.将变量声明为final 3.编译 4.将“对临时赋值”之后的右侧部分提炼得到一个独立对的函数中 5.编译、测试 6.在变量的实施Inline Temp |
Introduce Explaining Variable(124) 引入解释性变量 | 1.表达式过于复杂,引入解释变量提升可读性 2.为Extract Method 、Replace Temp with Query和 Replace Method with Mehod Oject梳理逻辑 | |
Splite Temporary Variable(128) 分解临时变量 | 每个临时变量有各种不同的作用,保证变量的单一赋值 | |
Remove Assignments to Parameters(131) 移除对参数的赋值 | 对参数赋值通常意味着副作用 | |
substitute Algorithm(139) 替换算法 | 如果你发现一件事有更简单的、更清晰点的方式。就应该以比较清晰的方式取代复杂的方式 |
2. 在对象之间搬移特性
重构手段 | 动机 | 做法 |
---|---|---|
Move Method(142) 搬移函数 | 1.一个类有太多行为,或者与另一个类有太多合作形成高度耦合 2. 寻找这样的函数,使用另一个对象的次数比自己所驻对象的次数还多 | 1.检查类中的被元函数所使用的一起特性(包括字段和函数),考虑一起搬移 2.检查源类的子类和超类是否有该函数的其他声明(若存在,目标类也表现出多态,不然无法搬迁) 3.在目标类中声明这个函数,也可以重新命名 4.编译目标类 5.决定如何从源函数正确引用目标类 6.修改源函数,使之成为一个纯委托函数 7.编译,测试 8.决定是否删除源函数,或将它当做一个委托函数保留下来 9.将源类中对源函数的所有调用者替换为目标函数的调用 10.编译,测试 |
Move Field(146) 搬移字段 | 1.一个字段,在其所驻的类之外的另一个类中更多函数使用它,也可能set和get函数,间接依赖。 2. 移动字段还是函数?取决于移动该字段的用户(某个使用该字段的函数),这取决于是否需要保持接口不变 | 1.如果字段的访问级别是public使用Encapsulate Field(206)封装字段将它封装起来 2.如果有可能移动那些频繁访问字段的函数,或如果有多个函数范文字段,优先使用Self Encapsulate Field(1171)自封装字段 |
Extract Class(149) 提取类 | 类会不断成长,不断的加入一些字段,加一些方法 2. 如果某些数据和方法一起出现,某些数据经常同时变化甚至彼此依赖,这就表示你应该将他们分离出去 3. 后期开发出现的信号是子类化的方式。如果子类子化只影响类的部分功能,或者你发现某些特性需要一种子类化,某些特性需要另一种子类化 | 1.分解类的职责 2.新建类,以表现从旧类中分离出来 |
Inline Class(154) 将类内联化 | 与Extract Class 相反,一个类没有足够的职责,或者重构移走了字段和方法 | |
Hide Delegate(157) 隐藏委托关系 | XXXXXXXXXXXXXX | |
Remove Middle Man(160) 移除中间人 | XXXXXXXXXXXXXX |
3 重新组织数据
重构手段 | 动机 | 做法 |
---|---|---|
Self Encapsulate Field(171) 自封装字段 | 使用setter和getter,访问和写字段 | |
Replace Data value with Object(175) 以对象取代数据值 | 开发初期可能只需要一个字段,后期面对这个数据不简单了 | |
Change value to Reference(179) 改变值对象为引用 | 确保对一个对象的修改作用到所有引用此对象的地方 | |
Change reference Reference to Value (183) 改变值对象为引用 | 引用对象必须以某种方式控制,你总是必须向控制着请求适当的引用对象,可能造成内存全区域之间错综复杂 | |
Replace Array with Object (186) 以对象替换数组 | 一个数组里面存放了同种类的数据,这时应考虑使用对象替换数组 | |
Duplicate Observed Data (189) 复制“被监控数据” | 将界面与业务分离,同时同步相关数据:1.有助于维护和开发;2.两种职责会使类更加复杂 | 引入观察者模式 |
Change Unidirectional Association to Bidirectional (197) 将单项关联该为双向关联 | 两个类之间建立一条单向对的引用链,一个类可以引用另一个类,随着时间推移,你发现需要被引用的类需要得其引用者,进行处理,也就是一个反向指针 | 1.在被引用类中,新加一个字段,用于保持引用指针 2.确定控制关联-有顶哪个类是引用端还是被引用端 |
Change Unidirectional Association to Bidirectional (197) 将单项关联该为双向关联 | 两个类之间建立一条单向对的引用链,一个类可以引用另一个类,随着时间推移,你发现需要被引用的类需要得其引用者,进行处理,也就是一个反向指针 | 1.在被引用类中,新加一个字段,用于保持引用指针 2.确定控制关联-有顶哪个类是引用端还是被引用端 3.在被控端建立一个辅助函数,其命名应该清楚指出2它的有限用途 4.xxxxxxxxxxxxxxxxx |
Change Bidirectional Association to Unidirectional (200) 将双向关联该为单项关联 | xxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxx |
Replace magic number with Symbolic Constant (204) 使用字面常量取代魔法数 | ||
Encapsulate Field (206) 封装字段 | ||
Encapsulate Collection (208) 封装集合 | 1.直接返回集合的引用,客户修改了集合的引用,集合拥有者一万无所知 2.不应该为集合提够一个设置函数 | 1.返回集合本身是,应使用Collections.unmodifableSet等去包装。 |
Replace Type Code with Class (218) 以类取代类型码 | 1.任何接受类型码作为参数的函数,期望的实际是一个数值,无法强制使用符号名,这会打打降低代码的可读性 2.把数值替换一个类,在编译期间对对对类型检查们可以保证参数合法时,实例才会被创建出来 3.保证类型码是纯粹的数据时(不应在switch的等语句中) | |
Replace Type Code with SubClass (223) 以子类取代类型码 | 1.类型码不影响宿主类的行为时,可以使用Replace Type code with Class,那么最好的办法借助多态来实现 2.有两种情况不能使用:(1).类型值在对象创建了以后发生了变化(2).宿主类已经有了子类。这两只情况下,可以使用Replace Type code with State/Strategy 3.宿主类出现了“只具备特定类型码之对象相关的特性”。在此之后,可以继续使用Push Down Method(328)和push down Field(329)将这些特性推到子类中 | |
Replace Type Code with State/Strategy (227) 以State/strategy取代类型码 | 1.如果“类型码的值在对象生命中发生变化”或其他宿主类不能被继承 2.如果采用此方法重构后,采用Replace Condition with Polymorphism(255)简化一个算法,那么选择Strategy比较合适 3.如果你打算搬移状态相关的数据,把新建的对象视为一种变迁状态,就应该选择使用state模式 | xxxxxxxxxxxxxxxxxxxxxxxx |
Replace SubClass whith Field (232) 以字段取代子类 | 子类存在,只提够个别硬编码的常量的功能,应该消除子类,在超类中,新建字段替换 | |
Replace SubClass whith Field (232) 以字段取代子类 | 子类存在,只提够个别硬编码的常量的功能,应该消除子类,在超类中,新建字段替换 |
4 简化条件表达式
重构手段 | 动机 | 做法 |
---|---|---|
Decompose Conditional(238) 分解表达式 | ||
Consolidate Conditional(240) 合并条件表达式 | 1.检查同一目的和结果。2.为Extract Mehod做准备 | |
Consolidate Duplicate Conditional Fragments(243) 合并重复的条件片段 | 不同分支都执行了相同的某段代码,把重复代码合并 | |
Remove Control Flag(245) 移除控制标记 | 代码中加入讨厌的控制标志符,编程语言提够break和continue就是跳出复杂的表达式 | |
Replace Nested Conditional with Guard clauses(250) 以卫语句取代嵌套条件表达式 | 多层嵌套,通常意味着特殊处理,这时恰好与卫语句的定义一致,如果使用if-else通常表示分支同样重要 | |
Replace Conditional with Polymorphism(250) 以多态替代条件表达式 | 同一个组条件表达式在程序许多地点出现,那么采取多态收益最大。 | |
Introduce Null Object(260) 引入Null对象 | xxxxxxxxxxxxxxx | |
Introduce Assertion(267) 引入断言 | 当某一块代码,只有某个条件为真是该段代码茶能正常运行,需要断言检查 |
5 简化函数调用
重构手段 | 动机 | 做法 |
---|---|---|
Rename Method(273) 函数改名 | 当你无法给函数其一个好名字,立即修改其名字 | |
Add Parameter(275) 添加参数 | 当你无法给函数其一个好名字,立即修改其名字 | |
Add Parameter(275) 添加参数 | ||
Remove Parameter(277) 移除参数 | ||
separate Query from Modifier(279) 将查询函数和修改函数分离 | 即有返回值又有副作用的函数,将其拆解为一个查询和一个修改的函数 | |
Parameterize Method(283) 令函数携带参数 | 若干函数做了类似的工作,但在函数本体中包含了不同的值,以参数粒度划分不同功能 | |
Replace Parameter with Explicit Method(285) 已明确函数取代参数 | 以方法粒度划分不同功能 | |
Preserve Whole Object(288) 保持对象完整 | 1.参数过长 2.提高可读性 | |
Replace Parameter with Method(292) 以函数取代参数 | xxxxxxxxxxxxxxxxxx | |
Introduce Parameter Object(295) 引入参数对象 | ||
Remove Setting Object(300) 移除设值函数 | ||
Hide method(300) 隐藏函数 | 吧函数设置为private | |
Replace Constructor with Factory Method(304) 以工厂函数取代构造函数 | xxxxxxxxxxxxxxxx | |
Encapsulate Downcast(308) 封装向下转型 | java是强类型语言,你必须告诉编译器确切的类型。 | |
Replace Error Code with Exception(310) 以异常取代错误码 | 已经可以清晰表达"普通程序"和"错误处理"分开 | |
Replace Exception with Test(315) 以测试代替异常 | 异常不可以取代条件检查 |
6 处理概括关系
处理继承的关系。
重构手段 | 动机 | 做法 |
---|---|---|
Pull Up Field(320) 字段上移 | 子类字段重复,将字段移动到超类 | |
Pull Up Method(322) 函数上移 | 1.子类中函数相同或者覆盖后与超类函数一致 2.被提升的函数引用了只出现在子类的函数,可以将这个函数一起提到超类 3.如果函数相似,可以先借助From Template Method(345) | |
Pull Up Construct Body(325) 构造函数本体上移 | 1.子类构造有共同的行为,第一念头就是将共同行为提炼到一个独立函数中 | |
Pull Down Method(328) 函数下移 | 超类中的某个函数只与部分子类有关 | |
Pull Down Filed(329) 字段下移 | 超类中的某个字段只与部分子类有关 | |
Extract Subclass (330) 提炼子类 | 类中的某些特性只被某些实例用到 | |
Extract Superclass (336) 提炼超类 | 两个类以相同的方式做类似的事情,对象提够了一种简化方式--继承 | |
Extract Interface (341) 提炼接口 | 与Extract Superclass相比。1.每个类只有一个超类2.对职责更清晰3.依赖更清晰 | |
Collapse Hierarchy (344) 折叠继承关系 | 子类化并未带来价值 | |
Form Template Method (345) 塑造模板函数 | 两个函数以相同顺序执行大致相近的操作,但不完全相同,将执行顺序移至超类,并借助多态保证各种操作保证差异性 | |
Replace Inheritance with Delegation (352) 以委托取代继承 | 从超类继承了一堆不需要的成员或函数 | |
Replace Delegation with Inheritance (355) 以继承取代委托 | 1.受委托的所有函数和字段都使用了,应该用继承取代委托 2.受托对象的所有字段方法没有被调用,就不能采取此方法 3.受托对象不止一个请其他对象共享,而且受委托对象时可以变的,就不能采取此方法 |
参考
《重构-改善既有代码的设计》Martin Fowler