《重构-改善既有代码的设计》 之 “从新组织函数” 笔记java
总共罗列了9种方法。算法
1.Extract Method (提炼函数)函数
将一段代码放到一个独立的函数中,并让函数名称解释该函数的用途。spa
--------------------------------------------------------------------设计
无局部变量:直接提炼出来,放到目标函数中。对象
--------------------------------------------------------------------blog
有局部变量(包括原函数参数、原函数中临时变量) 这里只分析临时变量。排序
Situation 1:被提炼代码段 “只读,不修改”局部变量。继承
Solution:局部变量 做为参数 传给目标函数。it
Situation 2:被提炼代码段,对局部变量赋值。
Ps:这个要分状况讨论。
Situation 2.1:该局部变量 只在被提炼代码段中 使用。
Solution for 2.1:将该局部变量的声明 移到被提炼代码段中,一块儿提炼出来。
Situation 2.2:该局部变量 在被提炼代码段外 还被使用到。
Ps:两种状况。
Situation 2.2.1:该变量 在被提炼代码段后 未被使用。
Solution for 2.2.1:直接在目标函数中修改该变量(变量可传参进来)。
Situation 2.2.2:该变量 在被提炼代码段后 被使用。
Solution for 2.2.2:目标函数 须要一个返回值,返回该变量被修改后的值。
Ps:若是 在被提炼代码段前 对该变量作了其余操做(不仅仅是明确初始化为0什么的),那就必须将该变量做为参数传给目标函数。
--------------------------------------------------------------------
2.Inline Method(内联函数)
解释:一个函数的 本体和名称 一样清楚易懂的状况下,适用该方法。
用法:在函数调用点直接插入函数本体。
Situation 1:即 “适用于”所述。
Solution:函数本体 替换 函数调用处。
Situation 2:函数不合理(包括内部结构、函数间调用复杂)。
Solution:先将不合理的函数内联到一个大型函数中,再从中提炼合理的小型函数。
Ps:
(a)这种状况下,内联函数只是做为辅助手段使用。
(b)若是子类继承了这个函数,那父类中就不能内联该函数了。
3.Inline Temp(内联临时变量)
解释:将全部对该变量的引用操做,替换为对它赋值的那个表达式。
Ps:若是该临时变量妨碍了重构,能够用Inline Temp方法内联化,再使用其余重构方法。
这个方法通常做为Replace Temp With Query(以查询取代临时变量)的一部分使用。
-----------------------------------------------------------------------
举个例子:某个临时变量被赋值为某个函数调用的返回值。
int a = func(); if (a > 0) {// do something}
通常为了检查这个临时变量是否只被赋值一次,能够将该变量声明为final,编译下。
final int a = func(); //而后没问题的话,再根据这个方法修改成: if (func() > 0) {// do something}
-----------------------------------------------------------------------
4.Replace Temp With Query(以查询取代临时变量)
解释:程序中用一个临时变量保存某一表达式的运算结果。
作法:将这个表达式提炼到一个独立函数中,而后用这个函数替换全部对临时变量的引用。
Ps:固然,这种重构方法较为简单的状况是:临时变量只被赋值一次或者赋值给临时变量的表达式不受其余条件影响。其余状况可能就须要先用其余重构方法处理下,而后再替换临时变量。
例子的话参考Inline Temp中的例子。其实Replace Temp With Query方法包含Inline Temp方法:
Inline Temp方法就是 一个替换的过程;Replace Temp With Query方法是提炼、替换的过程。
5.Introduce Explaining Variable (引入解释型变量)
解释:将复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。
例子:
int a = 1, b = 2, c = 3; if ( (a & b) > 0 && (b | c) != 3 || (-c & a) < 2) {// do something} //修改成 boolean isConditionA = ((a & b) > 0); boolean isConditionB = ((b | c) != 3); boolean isConditionC = ((-c & a) < 2); if ( isConditionA && isConditionB || isConditionC ) {//do something}
Ps:不过临时变量的局限性比较大,而函数复用性高,通常建议仍是先用Extract Method处理。若是Extract Method方法要处理一个大量局部变量的算法,那么就能够用本方法处理,而后再想下一步。
6.Split Temporary Variable(去除临时变量)
前提:程序中某个变量被赋值超过一次,它既不是循环变量,也不用于收集计算结果。
作法:针对每次赋值,建立一个独立、对应的临时变量。
除了循环变量和收集结果的变量外,每一个局部变量都应该只承担一个责任。同个变量承担的责任多了,会让阅读者感受糊涂。
例子:
int func(int first, int last) { int base = first + last; int sum = base * (last - first + 1); //第一次赋值 int halfSum = sum / 2; sum = halfSum; //第二次赋值 return sum; }
思路:
针对某一个被赋值超过屡次的变量,首先在其第一次被赋值处修改变量名,并定义为final;而后把第二次赋值前对sum变量的引用所有修改成对新变量firstSum的引用。以下:
int func(int first, int last) { int base = first + last; final int firstSum = base * (last - first + 1); //第一次赋值 int temp = firstSum + 1; //这里表示其余操做 sum = temp / 2; //第二次赋值 return sum; }
而后同理,在第二次赋值处修改变量名,及定义为final。同上述处理。
int func(int first, int last) { int base = first + last; final int firstSum = base * (last - first + 1); //第一次赋值 int temp = firstSum + 1; final int secondSum = temp / 2; //第二次赋值 return secondSum; }
这样就能想到不少其余重构方法来处理了。
7.Remove Assignment to Parameters(移除对参数的赋值)
前提:代码对一个参数 赋值。
作法:以一个临时变量取代该参数的位置。例外:要使用“出参”。
为何要这么作:若是直接对参数进行处理,会下降代码的清晰度,而且可能会混淆按值传递和按址传递。
8.Replace Method with Method Object(以函数对象代替函数)
前提:目前有一个大型函数,其中对局部变量的使用使你没法采用Extract Method。
作法:将这个函数放到一个单独对象中,这样局部变量就成了对象内字段了。而后能够在同一个对象内分解该大型函数。
主要步骤:
1.建立新类A(命名最好跟要处理的大型函数有关)
2.在A类中创建一个final字段,用来保存原先大型函数所在的对象(记为obj)。
针对原函数的每一个临时变量及参数,建立对应的字段保存。
3.建立构造函数,接收obj对象和原函数的全部参数做为参数。
4.建立一个方法(compute),复制原函数。若是须要调用原来对象的方法,就经过构造函数中传进来的obj对象来调用。
5.替换原函数。
这样就能够轻松的使用Extract Method方法了。
9.Substitute Algorithm(替换算法)
前提:你想把某个算法替换为另外一个更清晰的算法。
作法:将函数本体替换为另外一个算法。
例子:
修改前 |
修改后 |
int sort() { 冒泡排序; } |
int sort() { 归并排序; }
|
Over~~