您的位置:首页 > 其它

重构--Extract Method(提炼函数)(四)

2017-08-15 21:30 381 查看
重构方式
将下面的代码放进一个独立函数中,并让函数名称解释该函数的用途

void printOwing(double
amount){

printBanner();
//print details
System.out.println("name:"+_name);
System.out.println("amount"+amount);
}

void printOwing(double
amount){

printBanner();
printDetails(amount);
}

  

void printDetails(double
amount){

System.out.println("name:"+_name);
System.out.println("amount"+
amount);
}

动机

当出现一个过长的函数或者一段需要注释才能让人理解用途的代码,都可以将这段代码放进一个独立函数中;
函数粒度很小,函数被复用的机会就更大;
可以使高层函数读起来像一系列注释;
如果函数都是细粒度,那么函数的覆写也会更容易些;

做法
创建一个新函数,根据这个函数的意图来对它命名(以它"做什么"来命名,而不是以它"怎样做"命名);
将提炼出的代码从源函数复制到新建的目标函数中;
仔细检查提炼出的代码,看看其中是否引用了"作用域限于源函数"的变量(包括局部变量和源函数参数);
检查是否有"仅用于被提炼代码段"的临时变量。如果有,在目标函数中将它们声明为临时变量;
检查被提炼代码段,看看是否有任何局部变量的值被它改变。如果一个临时变量被修改了,看看是否可以将被提炼代码段处理为一个查询,并将结果赋值给相关变量。如果很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动地体谅出来。你可能需要先"分解临时变量",然后再尝试提炼。也可以用"以查询替代临时变量"将临时变量消灭掉;
将被提炼代码段中需要读取的局部变量,当作参数传给目标函数;
处理完所有局部变量之后,进行编译;
在源函数中,将被提炼代码段替换为对目标函数的调用;

范例:无局部变量
问题函数:

void printOwing(){

Enumeration
e = _orders.elements();
double
outstanding = 0.0;
//print banner
System.out.println("*******************");
System.out.println("***Customer Owes***");
System.out.println("*******************");
//calchlate outstanding
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
outstanding +=
each.getAmount();
}
//print details
System.<
142cc
strong>out[/b].println("name:" + _name);
System.out.println("amount" +
outstanding);
}

提炼出"打印横幅"的代码:

void printOwing(){

Enumeration
e = _orders.elements();
double
outstanding = 0.0;
printBanner();
//calchlate 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("*******************");
}

范例:有局部变量
第一种情况:被提炼代码段只是读取这些变量的值,并不修改它们。这种情况下,可以简单地将它们当作参数传给目标函数。这种方式可以处理多个局部变量或者该变量是个对象:

void printOwing(){

Enumeration
e = _orders.elements();
double
outstanding = 0.0;
printBanner();
//calchlate 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 printOwing(){

Enumeration
e = _orders.elements();
double
outstanding = 0.0;
printBanner();
//calchlate outstanding
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
outstanding +=
each.getAmount();
}
printDetails(outstanding);
}

  

void printDetails(double
outstanding) {

//print details
System.out.println("name:" + _name);
System.out.println("amount" +
outstanding);
}

第二种情况(对局部变量再赋值):此情况只讨论被赋值的临时变量;
示例代码:

void printOwing(){

Enumeration
e = _orders.elements();
double
outstanding = 0.0;
printBanner();
//calchlate outstanding
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
outstanding +=
each.getAmount();
}
printDetails(outstanding);
}

问题1:如果源函数的参数被赋值,请马上"移除对参数的赋值"!

被赋值的临时变量分两种情况:
该变量只在被提炼代码段中使用。可以将这个临时变量的声明移到被提炼代码段中,然后一起提炼出去;

被提炼代码段之外的代码也使用了这个变量,分两种情况:
如果这个变量在被提炼代码段之后,未再被使用,只需直接在目标函数中修改它就可以了;
如果被提炼代码段之后的代码还使用了这个变量,需要让目标函数返回该变量改变后的值;

了解以上情况后,现在尝试将"计算"代码提炼出来:

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变量e只在被提炼代码段中用到,所以可以将它整个搬到新函数中。double变量outstanding在被提炼代码段内外都被用到,所以必须让提炼出来的新函数返回它。编译通过后,把回传值改名:

double getOutstanding() {

Enumeration
e = _orders.elements();
double
result = 0.0;
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
result +=
each.getAmount();
}
return
result;
}

outstanding变量只是很单纯地被初始化为一个明确初值,所以可以只在新函数中对它初始化。如果代码还对这个变量做了其他处理,就必须将它的值作为参数传给目标函数。对于这种变化,最初的代码可能是这样:

void printOwing(double
previousAmount){

Enumeration
e = _orders.elements();
double
outstanding =
previousAmount * 1.2;
printBanner();
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
outstanding +=
each.getAmount();
}
printDetails(outstanding);
}

提炼代码:

void printOwing(double
previousAmount){

double
outstanding =
previousAmount * 1.2;
printBanner();
outstanding = getOutstanding(outstanding);
printDetails(outstanding);
}

double getOutstanding(double
initialValue) {

double
result = 0.0;
Enumeration
e = _orders.elements();
while(e.hasMoreElements()){
Order
each = (Order)e.nextElement();
result +=
each.getAmount();
}
return
result;
}

通过编译后,将变量outstanding的初始化过程进行整理:

void printOwing(double
previousAmount){

printBanner();
double
outstanding = getOutstanding(previousAmount * 1.2);
printDetails(outstanding);
}

问题:需要返回的变量不止一个,该怎么办?
挑选另一块代码来提炼。尽可能让函数都只返回一个值,用多个函数来返回多个值;

写在最后
临时变量往往为数众多。这种情况下,可以尝试"以查询取代临时变量"的方式减少临时变量。如果这样做无效,可以尝试"以函数对象取代函数";
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  重构