1:重复的代码(不仅仅是函数里,还有类之间)
Extract Method是我最常用的重构手法之一。当我看见一个过长的函数或者一段需要注释才能让人理解用途的代码,我就会将这段代码放进一个独立函数中。
有数个原因造成我喜欢简短而有良好命名的函数。首先,如果每个函数的粒度都很小(finely grained),那么函数之间彼此复用的机会就更大;其次,这会使高层函数码读起来就像一系列注释;再者,如果函数都是细粒度,那么函数的覆写(overridden)也会更容易些。
而且只有当你能给小型函数很好地命名时,它们才能真正起作用,所以你需要在函数名称下点功夫。人们有时会问我,一个函数多长才算合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离(semantic distance )。如果提炼动作 (extracting )可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码 还长也无所谓。
(有点累了,明天再看。)
继续:
1:原来的代码
void printOwing() {
Enumeration 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 = (Order) e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
这个方法原来是这个样子的,按照作者的说法,不在乎多少代码,而是在于函数之间的语义的距离,所以,首先把打印banner语句提炼出来。
void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner();
// calculate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
void printBanner() {
// print banner
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
}
再往下走,打印details的两条语句用到了一个局部变量outstanding。那么提炼为一个带一个参数的函数,最后变为:
void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner();
// calculate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
printDetails(outstanding);
}
void printDetails (double outstanding) {
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
看上面的printOwing函数,计算变量值的那一段代码也可以提炼出来。只不过这个代码里面对于局部变量outstanding赋值了,所以要在提炼出来的方法里声明一个变量作为返回值:
void printOwing() {
printBanner();
double outstanding = getOutstanding();
printDetails(outstanding);
}
double getOutstanding() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
return outstanding;
}
因为Enumeration变量只是在提炼出来的代码中用到,所以整个搬到新函数里,这样的话,看printOwing这个函数,里面只是说明每一步做什么了,非常清晰。
如果outstanding这个变量不是明确的初始化为一个值,而是调用了其它的变量,那么就作为一个参数传进来。
void printOwing(double previousAmount) {
double outstanding = previousAmount * 1.2;
printBanner();
outstanding = getOutstanding(outstanding);
printDetails(outstanding);
}
double getOutstanding(double initialValue) {
double result = initialValue;
Enumeration e = _orders.elements();
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
result += each.getAmount();
}
return result;
}
然后再整理下,得到下面的精简的函数:
void printOwing(double previousAmount) {
printBanner();
double outstanding = getOutstanding(previousAmount * 1.2);
printDetails(outstanding);
}
综上,如果一个函数过于复杂,那么就运用这个方法进行提炼,记住,一个函数最好只是实现一个功能。