在《重构 改善既有代码的设计》中,重新组织函数一章中,非常精彩的小节,也是该部分的核心重构手法为Extract Method。本文主要是对Extract Method进行整理,温故而知新。
注意:该重构手法中局部变量的含义要十分明确,局部变量包括传进源函数的参数和源函数所声明的临时变量。
提炼函数的难点在于被提炼代码段中局部变量的处理。在原书P111页有这样的陈述。
检查被提炼代码段,看看是否有局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼代码段处理为一个查询,并将结果赋值给相关变量。如果很难这样做,或如果被修改的变量不止一个,你就不能将这段代码原封不动的提炼出来。先使用Split Temporary Variable、Replace temp with Query优化后再进行。
void printOwing(){
Enmeration e = _orders.elements();
double outstanding = 0.0;
// print banner
System.out.println("*************************");
System.out.println("***** Customer Owes *****");
System.out.println("*************************");
// calculate outstanding
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
outstanding += each.getAmount();
}
// print details
System.out.println("name: " + _name);
System.out.println("amount" + outstanding);
}
提炼打印横幅的代码,只需要剪切、粘贴、再插入一个函数调用即可
void printOwing(){
Enmeration e = _orders.elements();
double outstanding = 0.0;
// print banner
printBanner();
// calculate outstanding
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
outstanding += each.getAmount();
}
// print details
System.out.println("name: " + _name);
System.out.println("amount" + outstanding);
}
void printBanner(){
System.out.println("*************************");
System.out.println("***** Customer Owes *****");
System.out.println("*************************");
}
局部变量,包括传进源函数的参数和源函数所声明的临时变量。局部变量的作用域仅限于源函数,所以必须花费额外功夫去处理这些变量。
最简单的情况:被提炼代码只是读取这些变量的值,并不修改它们
例如上述代码片段中
// print details
System.out.println("name: " + _name);
System.out.println("amount" + outstanding);
中的局部变量outstanding,近读取,而未修改,则可以简单地把它们当作参数传给目标函数。
void printOwing(){
Enmeration e = _orders.elements();
double outstanding = 0.0;
// print banner
printBanner();
// calculate outstanding
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
outstanding += each.getAmount();
}
// print details
printDetails(outstanding);
}
//提炼后的代码
void printDetails(double outstanding){
System.out.println("name: " + _name);
System.out.println("amount" + outstanding);
}
void printOwing(){
// print banner
printBanner();
// calculate outstanding
double outstanding = getOutStanding();
// print details
printDetails(outstanding);
}
double getOutStanding(){
Enmeration e = _orders.elements();
double outstanding = 0.0;
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
outstanding += each.getAmount();
}
return outstanding;
}
可以看到outstanding随后仍要使用,因此需要返回计算后的值。这个变量(outstanding)只在被提炼代码段中使用,果真如此,可以将这个临时变量的声明移到被提炼代码段中,然后一起提炼过去。可以看到上述的优化也是这么做的把double outstanding = 0.0
也一起提炼到了函数getOutStanding()中。
随后将回传值改名为result
double getOutStanding(){
Enmeration e = _orders.elements();
double result = 0.0;
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
result += each.getAmount();
}
return result;
}
本例中outstanding变量只是很单纯的被初始化为一个明确初值,所以我可以在心函数中对它进行初始化。如果代码还对这个变量做了其他处理,就必须将它的值作为参数传给目标函数。
void printOwing(double previousAmount){
Enmeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
// print banner
printBanner();
// calculate outstanding
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
outstanding += each.getAmount();
}
// print details
printDetails(outstanding);
}
可以看到对参数outstanding进行了初值的处理,因此需要作为参数传给目标函数
void printOwing(double previousAmount){
double outstanding = previousAmount * 1.2;
// print banner
printBanner();
// calculate outstanding
outstanding = getOutStanding(outstanding);
// print details
printDetails(outstanding);
}
double getOutStanding(double initialVale){
double result = initialVale;
Enmeration e = _orders.elements();
while (e.hasMoreElements()){
Order each = (Oder) e.nextElement();
result += each.getAmount();
}
return result;
}
void printOwing(double previousAmount){
// print banner
printBanner();
// calculate outstanding
double outstanding = getOutStanding(previousAmount * 1.2);
// print details
printDetails(outstanding);
}
拥有短函数的对象会活得比较好,比较长。而且程序越长,越难理解。最终的效果是:你应该更积极的分解函数。我们遵循这样一条原则:每当感觉需要注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)来命名。因此百分之九十九的长河里,要把函数变小,只需要使用Extract Method。找到函数中适合集中在一起的部分,将它们提拉到一个新函数。通过这样不断的实践,可以增强代码的可读性,使得程序阅读取来非常简单,也增强了程序的可复用性。