参考资料:
第六章 重新组织函数
这一章节的主线是对函数进行调整,使得函数有合适的结构和可读性。可以参考该章节一开始作者的总结,感觉这段写得非常好,把整章的手法都串联起来了。
-
提炼函数
动机:如果每个函数的粒度都很小,那么函数被服用的机会就更大;读起来方便;
做法:麻烦的地方在于局部变量的处理,可以在提炼函数之前先对局部变量进行缩减,总结如下。
-
提炼函数和内联函数
这两个是相反的过程,但总结都是调整函数使之有合理的嵌套。 -
内联临时变量
动机:这个手法通常只是用来帮助其他手法去掉临时变量。
做法:内联代表去掉间接层,如下所示。
修改前
double basePrice = anOrder.basePrice();
return (basePrice > 1000)
修改后
return (anOrder.basePrice() > 1000)
- 以查询取代临时变量(=提炼函数+内联临时变量)
动机:临时变量可以在函数内进行共享,因此有了临时变量,就会有更多的程序片段想要用到这个临时变量,这样会使得函数变长。这里其实也提取了函数,所以跟提取函数一样的好处有——如果每个函数的粒度都很小,那么函数被服用的机会就更大。发现跟前面共享循环一样,这种共享会代理不同功能之间的耦合,共享虽然可能带来节俭,但是也带来了耦合,所以有时该浪费还是要浪费,浪费带来了各个功能之间的清晰界限。这种手法只适用于临时变量只被赋值一次,不然临时变量应该有很多其他复杂的功能。这个手法很容易引起担忧,因为可能在小小的代码片段中我们就查询了多次,但是大部分情况下我们还是没有必要担心性能的。
做法:先提取表达式再进行内联
修改前
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
修改后
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
...
double basePrice() {
return _quantity * _itemPrice;
}
-
引入解释性变量
动机:将一个表达式拆解为多个步骤,步骤之间用局部变量表示,增强程序的可解释性。这种手法跟上面的手法恰好相反,引入了更多的局部变量 ,同时作者评论到,他经常采用提炼函数然后用查询来,这样函数可以在整个对象都可以使用到,而局部变量只能在当前函数中使用,但提炼函数可能要处理很复杂的局部变量的问题,所以在这种情况下还是得用引入解释性变量。引入解释性变量可以为提炼函数做铺垫,理清程序逻辑。 -
分解临时变量
动机:除了循环变量(i, j这些)和结果收集变量(i += 1,字符串结合,写入流,结合等),不要让一个临时变量多次赋值,多次赋值意味着它承担了多个责任。 -
移除对参数的赋值
void aMethod(Object foo) {
foo.modifyInSomeWay(); // that's OK
foo = anotherObject; // trouble and despair will follow you
动机:作者说到这样可以回让你误解传进来的那个参数在被调用函数之外已经变成anotherObject
,这个我觉得还好。第二个是违反了参数的语义,参数就是被传递进来的东西,对参数赋值有点滥用变量的意思。
-
以函数对象取代函数
动机:大的函数可以用函数提炼得到更加清晰的代码,但有些函数中局部变量交互过于复杂,分解函数变得十分困难,这个时候可以使用函数对象,也就是对象。对象其实是数据跟数据的操作(方法/函数)的集合,这种手法将函数升级为了对象,把临时变量升级为成员变量(数据),方便后面进一步进行方法提取。 -
替换算法
有时可以直接重写整段代码,而不是进行重构,书上举的例子非常好。
修改前
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
return "Don";
}
if (people[i].equals ("John")){
return "John";
114
}
if (people[i].equals ("Kent")){
return "Kent";
}
}
return "";
}
修改后
String foundPerson(String[] people){
List candidates = Arrays.asList(new String[] {"Don", "John",
"Kent"});
for (int i=0; i<people.length; i++)
if (candidates.contains(people[i]))
return people[i];
return "";
}
总结:
- 减少局部变量的手法
- 以查询取代临时变量
- 内联临时变量
网友评论