第六章 重新组织你的函数
6.1 Extract Method(提炼方法)
对付过长函数,一般重要的重构方法就是Extract Method,他把一段代码从原先的函数中提取出来,放在单独的函数中。简洁而清晰,短小而精炼。
1 void printOwing (douoble amount) 2 { 3 printBanner(); 4 //print details 5 System.out.println(“name:”+_name); 6 System.out.println(“amount”+amount); 7 }
提炼后:
1 void printOwing (douoble amount) 2 3 { 4 5 printBanner(); 6 7 printDetails(); 8 9 } 10 11 Void printDetails(double amount) 12 13 { 14 15 System.out.println(“name:”+_name); 16 17 System.out.println(“amount”+amount); 18 19 }
Extract Method最大的困难就是处理局部变量,所以需要注意一下步奏。
做法:
1、创造一个新的函数,根据这个函数的意图来给他命名(以它[做什么]命名,而不是以它[怎么做]命名)。
2、将提炼出来的代码从源函数(source)拷贝到新建的目标函数(target)中。
3、仔细检查提炼出来的代码,是否引用了[作用域限于源函数]的变量(包括局部变量和源函数参数)。
4、检查是否有[仅用于被提炼代码]的临时变量。如果有,在目标函数中将他们声明为临时变量。
5、检查被提炼码,看看是否有任何局部变量的值被它改变,或者被修改的变量不止一个,就不能原封不动的提炼出来了。
6、将被提炼码中需要读取的局部变量,当做参数传给目标函数。
7、处理完所有的局部变量,在原函数中调用。
实例:
范例一:无局部变量
提炼前:
1 Void printOwing() 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=0.0; 8 9 10 11 //print banner 12 13 System.out.println(“*****************************”); 14 15 System.out.println(“********Customer Owes********”); 16 17 System.out.println(“*****************************”); 18 19 20 21 //calculate outstanding 22 23 While(e.hasMoreElements()) 24 25 { 26 27 Order each=(Order) e.nextElement(); 28 29 Outstanding+=each.getAmount(); 30 31 } 32 33 34 35 //print details 36 37 System.out.println(“name:”+_name); 38 39 System.out.println(“amount”+outstanding); 40 41 }
我们可以轻松的提炼出 print banner 的代码。
1 Void printOwing() 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=0.0; 8 9 print Banner(); 10 11 //calculate outstanding 12 13 While(e.hasMoreElements()) 14 15 { 16 17 Order each=(Order) e.nextElement(); 18 19 Outstanding+=each.getAmount(); 20 21 } 22 23 //print details 24 25 System.out.println(“name:”+_name); 26 27 System.out.println(“amount”+outstanding); 28 29 } 30 31 Void printBanner() 32 33 { 34 35 //print banner 36 37 System.out.println(“*****************************”); 38 39 System.out.println(“********Customer Owes********”); 40 41 System.out.println(“*****************************”); 42 43 }
范例二:有局部变量
局部变量最简单的情况是:被提炼码只是读取这些变量的值,并不修改它们,这种情况下可以简单地将它们当做参数传给目标函数。
提炼前:
1 Void printOwing() 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=0.0; 8 9 print Banner(); 10 11 //calculate outstanding 12 13 While(e.hasMoreElements()) 14 15 { 16 17 Order each=(Order) e.nextElement(); 18 19 Outstanding+=each.getAmount(); 20 21 } 22 23 //print details 24 25 System.out.println(“name:”+_name); 26 27 System.out.println(“amount”+outstanding); 28 }
提炼后:
1 void printOwing() 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=0.0; 8 9 print Banner(); 10 11 //calculate outstanding 12 13 While(e.hasMoreElements()) 14 15 { 16 17 Order each=(Order) e.nextElement(); 18 19 Outstanding+=each.getAmount(); 20 21 } 22 23 printDetails(outstanding); 24 25 } 26 27 Void printDetails(double outstanding) 28 29 { 30 31 System.out.println(“name:”+ _name); 32 33 System.out.println(“amount”+ outstanding); 34 35 }
处理多个局部变量也可以使用上述这种方法。
范例三:对局部变量再赋值
如果被提炼码对局部变量赋值,问题就变得复杂了,这里我们只讨论临时变量的问题。
被赋值的临时变量也分为两种情况。比较简单的情况是:这个变量只是在被提炼码中使用,这样,可以将这个临时变量的声明一道被提炼码中,然后一起提炼出去。另一种情况是:被提炼码之外的代码也是用了这个变量。
提炼前:
1 Void printOwing() 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=0.0; 8 9 print Banner(); 10 11 //calculate outstanding 12 13 While(e.hasMoreElements()) 14 { 15 16 Order each=(Order) e.nextElement(); 17 18 Outstanding+=each.getAmount(); 19 20 } 21 22 printDetails(outstanding); 23 24 }
提炼后:
1 void printOwing() 2 3 { 4 5 print Banner(); 6 7 Double outstanding=getOutstanding(); 8 9 printDetails(outstanding); 10 11 } 12 13 Double getOutstanding() 14 15 { 16 17 Enumeration e=_orders.elements(); 18 19 Ddouble outstanding=0.0; 20 21 While(e.hasMoreElements()) 22 23 { 24 25 Order each=(Order) e.nextElement(); 26 27 Outstanding+=each.getAmount(); 28 29 } 30 31 Return outstanding(); 32 33 }
Enumeration 变量e只是在被提炼码中使用到,所以可以将它整个搬到新函数中。Double 变量 outstanding在被提炼码内外都被用到,所以必须让提炼出来的新喊出返回它。
这个例子中,outstanding变量只是很简单的被初始化为一个明确的值,所以可以只在新函数中对它初始化。如果代码还对这个变量做了其他处理,就必须将它的值作为参数传给目标函数。对于这种变化,最初的代码可能是这样:
1 Void printOwing(double previousAmount ) 2 3 { 4 5 Enumeration e=_orders.elements(); 6 7 Ddouble outstanding=previousAmount * 1.2; 8 9 print Banner(); 10 11 //calculate outstanding 12 13 While(e.hasMoreElements()) 14 15 { 16 17 Order each=(Order) e.nextElement(); 18 19 Outstanding+=each.getAmount(); 20 21 } 22 23 printDetails(outstanding); 24 25 }
提炼后:
Void printOwing() { Ddouble outstanding=previousAmount * 1.2; print Banner(); Double outstanding=getOutstanding(outstanding); //Double outstanding=getOutstanding(previousAmount * 1.2); printDetails(outstanding); } Double getOutstanding(double initialValue)) { Ddouble outstanding=initialValue; Enumeration e=_orders.elements(); While(e.hasMoreElements()) { Order each=(Order) e.nextElement(); Outstanding+=each.getAmount(); } Return outstanding(); }