Extract Method( 提炼函数 )
void printOwing(double amount)
{
printBanner();
//print details
System.out.println( "name:" + _name);
System.out.println( "amount" + amount );
}
==>
void printOwing(double amount)
{
printBanner();
}
void printDetails(double amount)
{
System.out.println( "name:" + _name);
System.out.println( "amount" + amount );
}
动机:
Extract Method是最常用的重构手法之一。当我看见一个过长的函数或者一段需要注释才能让人理解用途的代码,我就会将这段代码放进一个独立函数中。
有数个原因造成我喜欢简短而有良好命名的函数。首先,如果每个函数的粒度都很小(finely grained),那么函数之间彼此复用的机会就更大;其次,这会使高层函数码读起来就像一系列注释;再者,如果函数都是细粒度,那么函数的覆写(override)也会更容易些。
一个函数多长才算合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离(semantic distance)。如果提炼动作(extracting)可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓。
作法:
1、创造一个新函数,名字是写明是做什么,而不是怎样做。
2、将提炼出的代码从源函数拷贝到新建的目标函数中
3、仔细检查提炼出的代码,看看其中是否引用了作用域(限于源函数)的变量(包括局部编号和源函数参数)。
4、检查是否有[仅用于被提炼码]的临时变量,如果有,在目标函数中将它们声明为临时变量。
5、检查被提炼码,看看是否有任何局部变量的值被它改变,如果一个临时变量值被修改了,看看是否可以将被提炼处理为一个查询,并将结果赋值给相关变量。
如果很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动地提炼出来,你可能需要先使用Split Temporary Variables,然后再尝试提炼,
也可以使用Replace Temp with Query将临时变量消灭掉
6、将被提炼码中需要读取的局部变量,当作参数传给目标函数
7、处理完所有局部变量之后,进行编译
8、在源函数中,将被提炼码替换为[对目标函数的调用]
如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在可以删除这些声明式了。
9、编译、调试
一、没有局部变量,直接拷贝成为一个子函数
void printOWing()
{
Enumeration e = _orders.elements();
double outstanding = 0.0
//print bannder
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
//calulate outstanding
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
==>
void printOWing()
{
Enumeration e = _orders.elements();
double outstanding = 0.0
//print bannder
printbanner();
//calulate outstanding
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
二、带局部变量
1、被提炼代码只读取不修改,用到的局部变量作为参数传入
void printOWing()
{
Enumeration e = _orders.elements();
double outstanding = 0.0
//print bannder
printbanner();
//calulate outstanding
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
printDetails( outstanding )
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
void printDetails(double outstanding )
{
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
2、对局部变量再赋值
(1) 变量只在被提炼码中使用,则将这个临时变量的声明式移到被提炼码中,然后一起提炼出去。
(2) 如果这个变量在被提炼码之后的代码未在被使用,只需直接在目标函数中修改它就可以了。
(3) 如果被提炼码之后的代码还使用了这个变量,你就需要让目标函数返回该变量
void printOWing()
{
Enumeration e = _orders.elements();
double outstanding = 0.0
//print bannder
printbanner();
//calulate outstanding
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
printDetails( outstanding )
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
void printDetails(double outstanding )
{
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
==>
void printOwing()
{
//print bannder
printbanner();
//calulate outstanding
double outstanding = getOutStanding();
//print details
printDetails( outstanding )
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
void printDetails(double outstanding )
{
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
double getOutStanding()
{
double outstanding = 0.0;
Enumeration e = _orders.elements();
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
return outstanding;
}
==>
double getOutStanding()
{
double result = 0.0;
Enumeration e = _orders.elements();
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
return result;
}
(4) 源代码对局部变量进行其他初始化,不是初始化为一个初始值,作为参数传入。
void printOWing( double previousAmount )
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
//print bannder
printbanner();
//calulate outstanding
while ( e.hasMoreElements() )
{
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
printDetails( outstanding )
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
void printDetails(double outstanding )
{
System.out.println( "name:" + _name )
System.out.pintln( "amount" + outstanding );
}
==>
void printOWing( double previousAmount )
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
//print bannder
printbanner();
//calulate outstanding
outstanding = getOutStanding( outstanding )
//print details
printDetails( outstanding )
}
void printbannder()
{
System.out.println("**************************************");
System.out.println("***********Customer Owes**************");
System.out.println("**************************************");
}
void printDetails(double outstanding )
{
System.out.println( "name:" + _name )
System.out.pintln( "amount" + 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 )
{
Enumeration e = _orders.elements();
//print bannder
printbanner();
//calulate outstanding
double outstanding = getOutStanding( previousAmount * 1.2 )
//print details
printDetails( outstanding )
}
最好一个函数返回一个值,如果局部变量太多,则用Replace Temp With Query 或者Replace Method with Method Object 。