代码改变世界

重新组织函数

2013-11-03 14:17  ...平..淡...  阅读(403)  评论(0编辑  收藏  举报

《重构-改善既有代码的设计》 之 “重新组织函数” 笔记

总共罗列了9种方法。

1.Extract Method (提炼函数)

将一段代码放到一个独立的函数中,并让函数名称解释该函数的用途。

--------------------------------------------------------------------

无局部变量:直接提炼出来,放到目标函数中。

--------------------------------------------------------------------

有局部变量(包括原函数参数、原函数中临时变量这里只分析临时变量。

Situation 1:被提炼代码段 “只读,不修改局部变量。

Solution:局部变量 作为参数 传给目标函数。

 

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~~