重构-重构手法列表

重构技巧列表

重新组织函数

技巧名称 说明 动机 备注
extract method 将代码放进独立的函数中,并让函数名解释该函数的用途 1.如果每个函数的粒度都很小,那么被复用的概率更大;2.高层的函数读起来像一系列注释(注释后的代码更容易提炼);3.函数粒度小也更容易被覆写 提炼函数
inline method 函数本体和名称一样通俗易懂,在函数调用点插入函数本体 提取函数固然好,但非必要的间接性会增加阅读成本 内联函数
inline temp 将所有对某个临时变量的引用替换为对他赋值的查询语句(不适合耗费性能的操作) 多半可视为replace temp with query 的一部分使用;妨碍其他重构手法,使用内联 内联临时变量
replace temp with query 程序以一个临时变量来保存某一表达式的运行结果,将表达式提炼到独立函数中去,将这个临时变量替换为对新函数的调用 1.临时变量都是暂时的,所以只能在所属函数内使用;2.因为使用域的限制导致想要再次访问该临时变量则需要重复劳作;3.该技巧可以给 extract method 做准备 已查询取代临时变量
introduce explaining variable 将一个复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途 1.表达式有可能非常复杂而难以阅读,临时变量可以将表达式分解为比较容易管理的形式;2.在条件语句中,可以用此重构手法将条件子句进行提炼 引入解释性变量
split temporary variable 你的程序有某个临时变量被赋值超过一次,它既不是循环变量也不是用于收集统计的,针对每次赋值创建一个独立变量 如果一个临时变量被赋值了多次,应该拆分成多个临时变量,确保只有一个单一职责;同一个临时变量承担多个职责会令阅读者糊涂 分解临时变量
remove assignments to parameters 代码对一个参数进行赋值,以一个临时变量取代该参数的位置 对参数进行赋值,会降低代码的清晰度 移除对参数的赋值
replace method with mehtod object 你有一个大型函数,其中对局部变量的使用使你无法采用extract method,将函数移动到一个类中,并将局部变量变成对象内字段,在同一个类中拆解函数 针对某个函数中局部变量泛滥成灾的现象 以函数对象取代函数
substitute algorithm 将函数本体替换成另一种算法 解决问题有好几种方法,解决一个问题有更清晰简单的方式时,替换成新的方法吧 替换算法

在对象之间搬移特性

技巧名称 说明 动机 备注
move method 有个函数与所驻类之外的另一个类交流更多,在最常引用该函数的类型建立一个行为一样的函数,将旧函数变成一个委托函数 重构理论的重要支柱,如果一个类的行为太多或者某个行为和另外的类高度耦合,那么这些行为就要考虑移动了(寻找这样的函数:使用另一个领域比使用所驻类领域对象还要多) 搬移函数
move field 程序中的某个字段被另一个类使用的次数更加频繁,将该字段搬移到使用次数更频繁的类中 随着系统的发展,你会发现自己需要新的类,并将现有的工作责任拖到新的类中 搬移字段
extract class 某个类做了两个类应该做的事情,新建一个类将字段和方法移动到新类中 一个类应该是清楚的抽象,处理一些明确的责任,随着系统的不断状态,类会变得过于复杂(先考虑职责,在将不同职责的字段和函数提炼到新的类中) 提炼类
inline class 某个类没有做太多的事情,将这种类所得的特性搬移到另一个类中 一个类不在承担足够的责任,不在有独立存在的理由(一开始庞大,可能由于重构导致单薄),将这个类的特性迁移到使用它最多的类中 将类内联化
hide delegate 客户通过一个委托类来调用另一个对象 调用客户不想因委托的变化而进行修改,在委托类上建立客户所需要的所有行数,客户可通过委托类调用另一个类 隐藏委托关系
remove middle man 某个类做了太多的简单委托,让客户直接调用受托类 针对 hide delegate 付出的代价,每当客户要使用受托类的新特性,我们不得不在中间服务中增加简单的函数,随着受托类越来越庞大,这一过程痛苦不堪 移除中间人
introduce local extension 你需要为服务提供一些额外函数,但是你无法修改这个类 继承这个类,并且利用父类的函数,提供额外的功能 引入本地扩展

重新组织数据

技巧名称 说明 动机 备注
self encapsulate field 即使在类内,为这个字段设置取值/设值方法,并以这些函数访问这些字段 截然不同的观点:1.类内可以自由访问,2.即使在类内也应该通过函数访问;先使用观点1,当访问存在装饰时使用观点2 自封装字段
replace data value with object 你有一个数据项,需要与其他数据和行为一起使用才有意义,将数据项变成对象 如果某个字段出现了特殊的行为,短时间可以分散在各个类中,但是坏味道从此开始,你需要将这个字段封装成类,将特殊的行为包装在内 以对象取代数据值
chanage value to reference 从一个类衍生出来的各种相等的实例,将他们替换成相同的对象 并不好区分,引用对象在系统中就是独一份的 将值对象改为引用对象
replace array to object 你有一个数组,其中的元素各自代表不同的东西 数组应该只用于以某种顺序容纳一组相似对象 以对象替换数组
duplicate observed data 有一些数据置于gui组件中,而领域函数需要访问这些数据,将数据复制一份到领域对象中,使用观察者模式更新这些数据 一个良好的系统,需要将界面数据和处理业务的代码分开,但有时候数据无法分割(比如基于swing的界面编程) 复制被监制的数据
replace magic number with sysbolic constant 魔法值,即有特殊意义,却又不能明确表现出这种意义的数字 以字面常量替换魔法值
replace type code with class 针对类型码数值的传参,编译器看到并进行检查的始终是背后的那个code,大大降低代码的可读性,从而成为bug之源 以类取代类型码
replace type code with subclass 有一个不可变的类型码,它会影响类的行为,以子类取代这个类型码 如果类型码不会影响行为,用上面方法(replace type code with class)即可;如果类型码会影响行为,最好是借助多态来处理变化行为 以子类取代类型码
replace type code with state/strategy 有一个影响类行为的类型码,在对象的生命周期中发生变化,无法通过继承去消除它 本重构和replace type code with subclass相似,但是类型码在对象生命周期中变化/宿主类无法被继承,可以使用该重构 以状态模式/策略模式替换类型代码
replace subclass with field 各个子类的唯一差别只在 返回常量函数 的差别上,在超类中增加一个相应的字段,然后销毁子类 增加子类的目的是为了增加新的特性/改变其行为,如果子类中只有常量函数,那么考虑去除这样的子类避免因继承带来的额外复杂性 以字段取代子类

简化条件表达式

技巧名称 说明 动机 备注
decompose conditional 你有一个复杂的条件语句,从段落中分别提炼出独立的函数 1.复杂的条件语句是导致复杂度上升的原因之一;2.针对不同的条件分支,我们可以写测试来执行不同的分支行为,但是不明白为什么这样执行;3.分解出独立的函数,并根据分支行为给函数命名,这样可以大大提升可读性 分解条件表达式
consolidate conditional expression 你有一系列条件表达式,都等到相同的结果 检查条件各不相同,但最终行为却一致,合并成一个条件并提炼成函数表达式 合并条件表达式
consolidate duplicate conditional fragments 在各条件分支中有着相同的一段代码 一组条件表达式的所有分支上都执行了同一段代码,将这段代码搬移到条件表达式外面 合并重复的条件片段
remove control flag 在一系列布尔表达式中,某个变量带有控制标记的作用(这个变量可以结束某个流程) java中可以使用continue/break语句替换 移除控制标记
replace nested conditional with guard clauses 函数中的条件逻辑使人难以看清正常的执行路径(条件语句嵌套) 条件表达有两种形式:1.条件语句所有分支都属于正常行为;2.条件中只有一种情况是正常行为,其他情况不常见;其中后者可以使用该重构 使用卫语句处理特殊情况
replace conditional with polymorphism 有个条件表达式,根据对象类型的不同而选择不同的行为 多态的存在可以使你不必编写明显的条件语句(但是继承也会增加复杂性,要预测行为是否经常增加减少来判断是否真的适合多态来替换) 以多态取代条件表达式
introduce assertion 某一段代码需要对程序状态作出某种假设 当条件为真时某段代码才能正常运行,引入断言来排除不正常的情况(先行判断不正常情况发生时是否确实当作异常处理) 引入断言

简化函数调用

技巧名称 说明 动机 备注
rename method 函数的名称未能揭示函数的用途 极力提倡的一种编程风格,将复杂的处理过程分解成小函数,但是如果处理不好使你费尽周折却弄不清楚这些小函数各自的职责,所以给函数一个通俗易懂的名字 函数改名
add parameter 某个函数的改动需要更多的信息 不多说 添加参数
remove parameter 某个函数已经不再使用某个参数 程序员不太愿意删除无用参数。千万不要打这样的如意算盘:多余的参数在程序中不会引发任何的问题,未来的时间可能还用得上 移除参数
separate query from modifier 某个函数既返回了对象状态值又修改了对象状态值 如果一个函数没有任何副作用,即只有单一职责的接口(对于某些业务查询和更新行为确实会存在并存),我们可以任意调用它而不用操心太多地方 分离查询函数和修改函数
paramaterize method 若干函数做了类似的工作 你可能会发现这样的函数:有着类似的行为,但是因为某些值导致行为些许的不同,将这些值作为参数传入合并这些行为类似的函数 令函数携带参数
replace paramater with explicit methods 有一个函数,行为完全取决与传入的参数 针对不同的分支行为建立不同的函数,调用方不用再考虑传入赋予参数什么值调用(与paramaterize method 恰恰相反) 以明确函数取代参数
preserve whole object 你从某个对象中取出若干值作为某个函数的参数 改用传整个对象,1.减少参数列表,长的参数列表也是降低代码可读性的祸首;2.函数需要多余信息时参数列表可以不用跟着变更; 保持对象完整
replace paramater with methods 对象调用某个函数,并将获得结果作为函数的参数 如果获取到的结果只是服务于该函数(后续程序中并没有使用到),那么去掉参数,函数内部直接调用前一个函数 以函数取代参数
introduce paramater object 某些参数总是自然的同时出现 如果某些参数总是出现的频率非常高,那么他们有可能是属于同一个类属性的,用对象包装它们 引入参数对象
hide methods 如果一个函数从来没有被其他类调用过 函数的可见度应该符合其作用域范围 隐藏函数

处理概括关系

技巧名称 说明 动机 备注
pull up field 两个子类拥有相同的字段 如果子类是分别开发的,或者在重构过程中组合起来的,可能存在相同的字段;1.字段名字相同,这种很好区分,2.字段的使用方式相同,这种需要观察字段在函数中的作用。既能够减少重复的字段,还可以将重复的行为提到超类中 字段上移
pull up method 有些函数在各个子类中产生相同的结果 避免重复行为是很重要的,虽然重复的行为也能照常运行,但是你就会面临修改了一个但是遗漏了另一个的风险 函数上移
push down method 某个超类中的函数只与部分的子类有关 函数下移
push down field 某个超类中的字段只与部分的子类有关 字段下移
extract subclass 类中的某些特性只被某些实例用到 类中的某些行为只被一部分实例用到,提炼子类也能更清晰地划分出业务行为 提炼子类
extract superclass 两个类有相似的特性 重复的代码是系统糟糕的东西。不同的地方重复同样的行为,你也会面临修改了一个但是遗漏了另一个的风险 提炼超类
extract interface 若干客户使用类接口的同一子集,或两个类实现的接口有相同的部分 提炼接口
collapse hierarchy 超类和子类无太大的区别 继承体系的存在可能使其变得过分复杂,如果一个继承体系没有存在的价值,合并是很好的处理方式 折叠继承体系
form template method 有若干子类,存在相似的行为,只是有些细节不相同 这个重构又是为了消除重复代码。使用模版方法模式,在超类中制定函数的统一行为,行为细节定义为抽象方法,子类各自实现 塑造模版方法
replace inheritance with delegation 某个子类只使用超类接口中的一部分,或者根本不需要继承而来的数据 子类可以只使用超类功能的一部分。但是这样代码传达的信息和你的意图南辕北辙;继承是有很好的东西,设计模式中强调多用包含关系,少用继承关系(继承体系庞大会造成难以增加新特性/行为) 以委托替代继承
replace delegation with inheritance 和上述重构手法相反 一般遇到某个类使用了受托类的所有函数,并且花了很大的力气编写了极其简单的委托函数 以继承替代委托

大型重构

技巧名称 说明 动机 备注
tease apert inheritance 少用继承,多用委托 庞大复杂的继承体系要小心(java 7 由于 Collections 复杂的继承体系,导致无法新增加特性,只能使用 default 关键字) 梳理并分解继承体系
converter procedural design to object 将过程化设计转为对象设计
separate domain from presentation mvc模式就是很好的例子,jsp就是反例 将领域对象和表述/显示分离
显示 Gitment 评论