主要针对于Long Methods 算法
主要重构手法:Extract Method 函数
Tips: 若是在进行屡次提炼以后,意识到提炼所得的某些函数并无作任何实质事情,或若是须要回溯回复原先函数,就须要Inline Method(将一个函数调用动做替换为该函数本体) 测试
Extract Method 最大的困难就是处理局部变量,而临时变量则是其中一个主要的困难源头。 spa
处理一个函数时,能够运用 Replace Temp with Query去掉全部可去掉的临时变量。若是不少地方使用了某个临时变量,先运用Split Temporary Variable将它变得比较容易替换 对象
若是临时变量实在太混乱,难以替换。这时候就须要使用Replace Method with Method Object , 它能够分解哪怕最混乱的函数,代价则是引入一个新的类。 继承
若是在函数内给参数进行了赋值,那么得使用Remove Assignments to Parameters 递归
函数分解完毕后,若是发现算法能够改进从而使代码更清晰,则可使用Substitute Algorithm 引入更清晰的算法。 ip
1. Extract Method (提炼函数)
Summary:
将一段代码放进一个独立函数中,并让函数名称解释该函数的用途 it
Motivation:
- 若是每一个函数的粒度都很小,那么函数被复用的机会就更大
- 这会使高成函数读起来就像一系列注释
- 若是函数都是细粒度,那么函数的复写也会更容易些
Mechanics:
- 建立一个新函数,根据这个函数的意图来对它命名(“作什么”而非“怎样作”)
- 将提炼出的代码从原函数复制到新建的目标函数中
- 仔细检查提炼出的代码,看看其中是否引用了“做用局限于原函数”的变量(包括局部变量和原函数参数)。
- 检查是否有“仅用于被提炼代码段”的临时变量。若是有,在目标函数中将它们声明为临时变量。
- 检查被提炼代码段,看看是否有任何局部变量的值被它改变。若是一个临时变量值被修改了,看看是否能够将被提炼代码段处理为一个查询,并将结果赋值给相关变量。若是很难这样作,或若是被修改的变量不止一个,你就不能仅仅将这段代码原封不动地提炼出来。你可能须要先使用Split Temporary Variable, 而后尝试提炼。也可使用Replace Temp with Query 将临时变量消灭掉。
- 将被提炼代码段中须要读取的局部变量,当作参数传给目标函数。
- 处理完全部局部变量后,进行编译。
- 在原函数中,将被提炼代码段替换为对目标函数的调用。
- 编译,测试。
2. Inline Method (内联函数)
Summary:
在函数调用点插入函数本体,而后移除该函数。 io
Motivation:
- 函数内部代码和函数名称一样清晰易读,去掉非必要的间接性。
- 有一群组织不甚合理的函数,能够将它们都内联到一个大型函数中,再从中提炼出组织合理的小型函数。实施Replace Method with Method Object以前先这么作,每每能够得到不错的效果。
- 若是别人使用的太多间接层,使得系统中的全部函数都彷佛只是对另外一个函数的简单委托,形成繁琐的委托动做,这时可使用Inline Method。试着使用内联手法,找出有用的间接层,移除无用的间接层。
Mechanics:
- 检查函数,肯定它不具多态性。(若是子类继承了这个函数,就不要将此函数内联,由于子类没法覆写一个根本不存在的函数。)
- 找出这个函数的全部被调用点。
- 将这个函数的全部被调用点都替换为函数本体
- 编译,测试。
- 删除该函数的定义。
Tips: 对于递归调用,多返回点,内联至另外一个对象中而该对象并没有提供访问函数等复杂状况,那么就不该该使用这个重构手法
3. Inline Temp(内联临时变量)
Summary:
将全部对该变量的引用动做,替换为对他赋值的那个表达式自身。
Motivation:
Inline Temp多半是做为Replace Temp with Query的一部分使用的,因此真正的动机出如今后者那儿。惟一单独使用Inline Temp的状况是:你发现某个临时变量被赋予某个函数调用的返回值。通常来讲,这样的临时变量不会有任何危害,能够放心地把它留在那儿。但若是这个临时变量妨碍了其余的重构手法,例如Extract Method,就应该将它内联化。
Mechanics:
- 检查给临时变量赋值的语句,确保等号右边的表达式没有反作用。
- 若是这个临时变量并未被声明为final,那就将它声明为final,而后编译。(这能够检查该临时变量是否真的只被赋值一次)
- 找到该临时变量的全部引用点,将它们替换为“为临时变量赋值”的表达式。
- 每次修改后,编译并测试。
- 修改完全部引用点以后,删除该临时变量的声明和赋值语句。
- 编译,测试。
4. Replace Temp with Query(以查询取代临时变量)
Summary:
将这个表达式提炼到一个独立的函数中。将这个临时变量的全部引用点替换为对新函数的调用。此后新函数就可被其余函数使用。
Motivation:
临时变量的问题在于:它们是暂时的,并且只能在所属函数内使用。因为临时变量只能在所属函数内可见,因此它们会驱使你写出更长的函数,由于只有这样你才能访问到须要的临时变量。若是把临时变量替换为一个查询,那么同一个类中的全部函数都将能够得到这份信息。这将带给你极大帮助,使你可以为这个类编写更清晰的代码。
Replace Temp with Query 每每是运用Extract Method以前必不可少的一个步骤。局部变量会使代码难以被提炼,因此应该尽量把它们替换为查询式。
这种重构手法较为简单的状况是:临时变量只被赋值一次,或者赋值给临时变量的表达式不受其余条件影响。其余状况比较棘手,但也有可能发生。可能须要先运用Split Temporary Variable 或Separate Query from Modifier 视状况变得简单一些,而后再替换临时变量。若是你想替换的临时变量是用来收集结果的(例如循环中的累加值),就须要将某些程序逻辑(如循环)复制到查询函数去。
Mechanics:
- 找出只被赋值一次的临时变量。(若是某个临时变量被赋值超过一次,考虑使用 Split Temporary Variable)将它分割成多个变量
- 将该临时变量声明为final(这可确保临时变量的确只被赋值一次)
- 编译
- 将“对该临时变量赋值”之语句的等号右侧部分提炼到一个独立函数中。(首先将函数声明为private。往后你可能会发现有更多类须要使用它,那时放松对它的保护也容易。其次,确保提炼出来的函数无任何反作用,也就是说该函数并不修改任何对象内容。若是它有反作用,就对它进行Separate Query from Modifier)
- 编译,测试
- 在该临时变量身上实施Inline Temp。