《Refactoring》: 重新组织函数 - beanlam
Extract Method(提炼函数)
package io.beansoft.refactory.reorganizemethod;import java.util.ArrayList;import java.util.List;/** * * * @author Bean * @date 2016年1月13日 下午5:49:50 * @version 1.0 * */public class ExtractMethod { // original void printOwing0() { List<Integer> list = new ArrayList<Integer>(); double outstanding = 0.0; //print banner System.out.println("**************************"); System.out.println("***Customer Owes**********"); System.out.println("**************************"); // calculate outstanding for (int num : list) {outstanding += num; } //print details System.out.println("**************************"); System.out.println("amount:" + outstanding); System.out.println("**************************"); } //没有局部变量 void printOwing1() { List<Integer> list = new ArrayList<Integer>(); double outstanding = 0.0; printBanner(); // calculate outstanding for (int num : list) {outstanding += num; } //print details System.out.println("**************************"); System.out.println("amount:" + outstanding); System.out.println("**************************"); }//有局部变量 void printOwing2() { List<Integer> list = new ArrayList<Integer>(); double outstanding = 0.0; printBanner(); // calculate outstanding for (int num : list) {outstanding += num; } printDetails(outstanding); } //对局部变量再赋值 void printOwing3() { double outstanding = 0.0; printBanner(); outstanding = getOutstanding(); printDetails(outstanding); } double getOutstanding() { double result = 0.0; List<Integer> list = new ArrayList<Integer>(); // calculate outstanding for (int num : list) {result += num; } return result; } void printDetails(double outstanding) { //print outstanding System.out.println("**************************"); System.out.println("amount:" + outstanding); System.out.println("**************************"); } void printBanner() { //print banner System.out.println("**************************"); System.out.println("***Customer Owes**********"); System.out.println("**************************"); }}
Inline Method(内联函数)
一个函数的本体与名称同样清楚易懂时使用
package io.beansoft.refactory.reorganizemethod;/** * * * @author Bean * @date 2016年1月13日 下午6:07:43 * @version 1.0 * */public class InlineMethod { private int _numberOfLateDeliveries = 0; int getRating() { return moreThanFiveLateDeliveries() ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }//重构后的版本 int getRating0() { return (_numberOfLateDeliveries > 5) ? 2 : 1; }}
Inline Temp(内联临时变量)
你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法,比如Extract Method。
double basePrice = anOrder.basePrice();return (basePrice > 1000);//重构后return (anOrder.basePrice() > 1000);
Replace Temp with Query(以查询取代临时变量)
你的程序以一个临时变量保存某一表达式的运算结果
double basePrice = quantity * itemPrice;if (basePrice > 1000) { return basePrice * 0.95;} else { return basePrice * 0.98;}//重构后if (basePrice() > 1000) { return basePrice() * 0.95;} else { return basePrice() * 0.98;}double basePrice() { return quantity * itemPrice;}
关于性能的问题如何考虑,如果basePrice()造成了性能低下,则把临时变量放回去
Introduce Explaining Variable(引入解释性变量)
你有一个复杂的表达式
if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 ) { // do something}//重构后final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;final boolean wasResized = resize > 0;if (isMacOs && isIEBrowser && wasInitialized() && wasResized) { // do something}
Split Temporary Variable(分解临时变量)
你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果
针对每次赋值,创造一个独立、对应的临时变量。
double temp = 2 * (height * width);System.out.println(temp);temp = height * width;System.out.println(temp);//重构后final double perimeter = 2 * (height * width);System.out.println(perimeter);final double area = height * width;System.out.println(area);
一个临时变量如果被赋值超过一次,就意味着它在函数中承担了一个以上的责任,就应该被分解为多个临时变量。
同一个临时变量承担两件不同的事情,会令代码阅读者糊涂。
Remove Assignments to Parameters(移除对参数的赋值)
代码对一个参数进行赋值
应该以一个临时变量取代该参数的位置。
int discount(int inputVal, int quantity, int yearToDate) { if (inputVal > 50) { inputVal -= 2; } }//重构后int discount(int inputVal, int quantity, int yearToDate) { int result = inputVal; if (result > 50) { result -= 2; } }
如果把一个名为foo的对象作为参数传递给某个函数,那么"对参数赋值"将会改变foo,使其引用另一个对象。
但这会降低代码的清晰度。而且之后如果在foo上进行操作,将不会改变原来的对象。
在Java中,参数传递是按值传递的。
Replace Method with Method Object(以函数对象取代函数)
如果你有一个大型函数,并且对局部变量的使用是你无法采用Extract Method对其重构
那么你应该将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段。
然后可以在同一个对象中将这个大型函数分解为多个小型函数(函数的入参也会当做对象内的字段)。
public class Account { public int calculate(int basePrice, double discount, int quantity) { int tempValue1 = basePrice * quantity + factor(); int tempValue2 = basePrice * quantity * discount; return tempValue1 + tempValue2; } public double factor() { return 0.99; }} //上面的程序也许不适合做一个例子,请先不管它的逻辑是什么//构建一个新类class Calculator { private final Account account; private int basePrice; private double discount; private int quantity; private int tempValue1; private int tempValue2; public Calculator(Account account, int basePrice, double discount, int quantity) { this.account = account; this.basePrice = basePrice; this.discount = discount; this.quantity = quantity; }}//接下来,修改Account的旧函数,将它的工作委托给刚完成的这个函数对象public class Account { public int calculate(int basePrice, double discount, int quantity) { return new Calculator(this, basePrice, discount, quantity); } public double factor() { return 0.99; }} //于是乎,Calculator的函数就可以这么设计了class Calculator { private final Account account; private int basePrice; private double discount; private int quantity; private int tempValue1; private int tempValue2; public Calculator(Account account, int basePrice, double discount, int quantity) { this.account = account; this.basePrice = basePrice; this.discount = discount; this.quantity = quantity; } public int calculator() { this.tempValue1 = basePrice * quantity + account.factor(); this.tempValue2 = basePrice * quantity * discount; return tempValue1 + tempValue2; }}//如果Calculator的calculate函数需要再一次重构,就可以在Calculator中进行重构了
Substitude Algorithm(替换算法)
你想把某个算法替换为另一个更清晰的算法。
那么就直接把函数体替换为另一个算法吧
String foundPerson(String[] persons) { for (int i = 0; i < persons.length; i++) { if (persons[i].equals("Don")) {return "Don"; } if (persons[i].equals("John")) {return "John"; } if (persons[i].equals("Kent")) {return "Kent"; } }}//对算法进行重构String foundPerson(String[] persons) { List candidates = Arrays.asList(new String[]{"Don", "John", "Kent"}); for (int i = 0; i < persons.length; i++) { if (candidates.contains(persons[i])) {return persons[i]; } }}
在进行算法重构之前,需要先确定自己已经充分了解原来的算法。