重构之重新组织函数(ExTract Method)
引用自 Refactoring Improving the Design of Existing Code ---Martin Fowler
1.Extract Method(提炼函数)
范例(Examples)
1、无局部变量(No Local Variables)
public class ExtractMethodWithoutLocalVariable { //1、无局部变量(No Local Variables) public void printUserInfoWithoutLocalVariables() { System.out.println("******************************"); System.out.println("************用户信息**********"); System.out.println("******************************"); } //改为 public void printUserInfoWithoutLocalVariablesAdvanced(){ printStart(); System.out.println("Name="+"王有财"+"Age="+"26"+"Sex"+"男性"); } public void printStart(){ System.out.println("******************************"); System.out.println("************用户信息**********"); System.out.println("******************************"); } }
2、有局部变量(Using Local Variables)
public class ExtractMethodWithLocalVariable { //1、有局部变量(No Local Variables) public void printUserInfoWithoutLocalVariables() { System.out.println("******************************"); System.out.println("************用户信息**********"); System.out.println("******************************"); String name = "王有财"; String age = "26"; String sex = "男性"; System.out.println("Name="+name+"Age="+age+"Sex"+sex); } //改为 public void printUserInfoWithoutLocalVariablesAdvanced(){ printStart(); String name = "spring yang"; String age = "26"; String sex = "Man"; printPersonalValue(name,age,sex); } public void printStart(){ System.out.println("******************************"); System.out.println("************用户信息**********"); System.out.println("******************************"); } public void printPersonalValue(String name,String age,String sex){ System.out.println("Name="+name+"Age="+age+"Sex"+sex); } }
3、对局部变量现赋值(Reassigning)
如果被提炼代码对局部变量赋值,问题就变得复杂了。这里我们只讨论临时变量的问题。如果你发现源函数的参数被赋值,应该马上使用Remove Assignments to Parameters。
public class ExtractMethodWithReassigningValue { //如果被提炼代码对局部变量赋值,问题就变得复杂了。 //如果你发现源函数的参数被赋值,应该马上使用Remove Assignments to Parameters。 public void printSum() { double result = 0; for (int i = 0; i <= 100; i++) result += i; System.out.println("From 1 add to 100 result is"+result); } public void printSumAdvanced(){ printInfo(sum()); } public double sum(){ double result = 0; for (int i = 0; i <= 100; i++) result += i; return result; } public void printInfo(double sum){ double result = sum; System.out.println("From 1 add to 100 result is"+result); } }
动机:
Extract Method (提炼函数)是最常用的重构手法之一。当看见一个过长的函数或者一段需要注释才能让人理解用途的代码,就应该将这段代码放进一个独立函数中。
简短而命名良好的函数的好处:
首先,如果每个函数的粒度都很小,那么函数被复用的机会就更大;其次,这会使高层函数读起来就想一系列注释;
再次,如果函数都是细粒度,那么函数的覆写也会更容易些。一个函数多长才算合适?长度不是问题,关键在于函数名称和函数本体之间的语义距离。如果提炼可以强化代码的清晰度,那就去做,就算函数 名称必提炼出来的代码还长也无所谓。
做法:
1、创造一个新函数,根据这个函数的意图对它命名(以它“做什么“命名,而不是以它“怎样做”命名)。
即使你想要提炼的代码非常简单,例如只是一条消息或一个函数调用,只要新函数的名称能够以更好方式昭示代码意图,你也应该提炼它。但如果你想不出一个更有意义的名称,就别动。
2、将提炼出的代码从源函数复制到新建的明白函数中。
3、仔细检查提炼出的代码,看看其中是否引用了“作用域限于源函数”的变量(包括局部变量和源函数参数)。
4、检查是否有“仅用于被提炼代码段”的临时变量。如果有,在目标函数中将它们声明为临时变量。
5、检查被提炼代码段,看看是否有任何局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼代码处理为一个查询,并将结果赋值给修改变量。如果很难这样做,或如果被修改的变量不止一个,你就不能仅仅将这段代码原封不动提炼出来。你可能需要先使用 Split Temporary Variable(分解临时变量),然后再尝试提炼。也可以使用 Replace Temp with Query(以查询取代临时变量)将临时变量消灭掉。
6、将被提炼代码段中需要读取的局部变量,当做参数传给目标函数。
7、处理完所有局部变量后,进行编译。
8、在源函数中,将被提炼代码段替换给对目标函数的调用。
如果你将如何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼代码段的外围。如果是,现在可以删除这些声明式了。
9、编译,测试。