提炼函数
重新组织函数的前提:过长函数Long Methods
。
有一段代码可以被组织在一起并独立出来。可以通过 Extract Method(提炼函数) 把一段代码从原先函数中提取出来,并让函数名称解释该函数的用途。
1 2 3 4 5 6 7 void printOwing () { printBanner(); System.out.println("name: " + name); System.out.println("amount: " + getOutstanding()); }
1 2 3 4 5 6 7 8 9 void printOwing () { printBanner(); printDetails(getOutstanding()); } void printDetails (double outstanding) { System.out.println("name: " + name); System.out.println("amount: " + outstanding); }
内联函数
有时候遇到某些函数,其内部代码和函数名称同样清晰易读,可能你重构了该函数,使得其内容和名称变得同样清晰,果子如此,你就应该通过 Inline Method(内联函数) 去掉这个函数。
1 2 3 4 5 6 7 8 9 class PizzaDelivery { int getRating () { return moreThanFiveLateDeliveries() ? 2 : 1 ; } boolean moreThanFiveLateDeliveries () { return numberOfLateDeliveries > 5 ; } }
1 2 3 4 5 6 class PizzaDelivery { int getRating () { return numberOfLateDeliveries > 5 ? 2 : 1 ; } }
内联临时变量
如果有一个临时变量,只被简单表达式赋值一次,而它妨碍了其他重构手法,那么可以通过 Inline Temp(内联临时变量) 对该变量所有的引用动作替换为对他赋值的那个表达式自身。
1 2 3 4 boolean hasDiscount (Order order) { double basePrice = order.basePrice(); return basePrice > 1000 ; }
1 2 3 boolean hasDiscount (Order order) { return order.basePrice() > 1000 ; }
以查询取代临时变量
如果有一个临时变量,只被简单表达式赋值一次,也可以通过 Replace Temp with Query(以查询取代临时变量) 将这个表达式提炼到一个独立函数中,将这个临时变量所有引用点替换为对新函数的调用,方便后续重构。 Inline Temp(内联临时变量) 多半是作为 Replace Temp with Query(以查询取代临时变量) 的一部分来使用,所以真正的动机出现在后者那儿(查询取代临时变量方便方法重构)。
1 2 3 4 5 6 7 8 9 double calculateTotal () { double basePrice = quantity * itemPrice; if (basePrice > 1000 ) { return basePrice * 0.95 ; } else { return basePrice * 0.98 ; } }
1 2 3 4 5 6 7 8 9 10 11 double calculateTotal () { if (basePrice() > 1000 ) { return basePrice() * 0.95 ; } else { return basePrice() * 0.98 ; } } double basePrice () { return quantity * itemPrice; }
引入解释性变量
当处理一个拥有大量局部变量的算法的时候,直接使用 Extract Method(提炼函数) 绝非易事。这种情况下可以使用 Introduce Explaining Variable(引入解释性变量) 来理清代码,梳理清楚逻辑之后,再使用 Replace Temp with Query(以查询取代临时变量) 把中间引入的解释性临时变量去掉,方便重构。况且,如果我最终使用 Replace Method with Method Object(以函数对象取代函数) ,那些引入的解释性临时变量也有其价值。
1 2 3 4 5 6 7 8 void renderBanner () { if ((platform.toUpperCase().indexOf("MAC" ) > -1 ) && (browser.toUpperCase().indexOf("IE" ) > -1 ) && wasInitialized() && resize > 0 ) { } }
1 2 3 4 5 6 7 8 9 void renderBanner () { final boolean isMacOs = platform.toUpperCase().indexOf("MAC" ) > -1 ; final boolean isIE = browser.toUpperCase().indexOf("IE" ) > -1 ; final boolean wasResized = resize > 0 ; if (isMacOs && isIE && wasInitialized() && wasResized) { } }
分解临时变量
假如有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果,那么可以使用 Split Temporary Variable(分解临时变量) 针对每次赋值,创造一个独立、对应的临时变量。
1 2 3 4 double temp = 2 * (height + width);System.out.println(temp); temp = height * width; System.out.println(temp);
1 2 3 4 final double perimeter = 2 * (height + width);System.out.println(perimeter); final double area = height * width;System.out.println(area);
移除对参数的赋值
函数的参数都应该被当成final类型来处理,假设代码中对一个参数进行了赋值,请通过 Remove Assignments to Parameters(移除对参数的赋值) 以一个临时变量取代该参数的位置。如果函数体很长,可以尝试在参数前添加final关键词,检查方法体中是否有对参数进行重新赋值。
1 2 3 4 5 6 int discount (int inputVal, int quantity) { if (inputVal > 50 ) { inputVal -= 2 ; } }
1 2 3 4 5 6 int discount (int inputVal, int quantity) { if (inputVal > 50 ) { inputVal -= 2 ; } }
以函数对象取代函数
如果有一个大型函数,变量太混乱,难以替换,其中对局部变量的使用是你无法采用 Extract Method(提炼函数) ,可以通过 Replace Method with Method Object(以函数对象取代函数) 将这个函数放进一个单独的对象中,如此一来,局部变量就编程了对象内的字段,这样就可以很方便的在同一个对象中将这个大型函数分解为多个小型函数了。
1 2 3 4 5 6 7 8 9 class Order { public double price () { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Order { public double price () { return new PriceCalculator (this ).compute(); } } class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice; public PriceCalculator (Order order) { } public double compute () { } }
替换算法
针对不够好的算法,可以使用 Substitute Algorithm(替换算法) 引入更加清晰的算法。
使用这项朝那个狗手法之前,请先确定自己已经尽可能分解了原先函数。替换一个巨大而复杂的算法是非常困难的,只有先降它分解为较简单的小型函数,才能更有把握地进行算法替换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 String foundPerson (String[] people) { for (int i = 0 ; i < people.length; i++) { if (people[i].equals("Don" )){ return "Don" ; } if (people[i].equals("John" )){ return "John" ; } if (people[i].equals("Kent" )){ return "Kent" ; } } return "" ; }
1 2 3 4 5 6 7 8 9 10 String foundPerson (String[] people) { List candidates = Arrays.asList(new String [] {"Don" , "John" , "Kent" }); for (int i=0 ; i < people.length; i++) { if (candidates.contains(people[i])) { return people[i]; } } return "" ; }