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 wasResize = resize > 0;
if(isMacOs && isIEBrowser && wasInitialized() && wasResize){
//do something
}
范例
//重构前
double price(){
// 价格 = basePrice - quantity discount + shipping
return _quantity * _itemPrice -
Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
Math.min(_quantity * _itemPrice * 0.25, 100);
}
//重构后
double price(){
// 价格 = basePrice - quantity discount + shipping
final double basePrice = _quantity * _itemPrice;
final double quantityDiscount = Math.max(0, _quantity - 800) * _itemPrice * 0.15;
final double shipping = Math.min(basePrice * 0.25, 100);
return basePrice - quantityDiscount + shipping;
}
运用Extract Method处理:
对于上述代码,通常不以临时变量来解释其动作意图,而是更喜欢使用提炼函数。
//重构后
double price(){
// 价格 = basePrice - quantity discount + shipping
return basePrice() - quantityDiscount() + shipping();
}
private double basePrice(){
return _quantity * _itemPrice;
}
private double shipping(){
return Math.min(basePrice() * 0.25, 100);
}
private double quantityDiscount(){
return Math.max(0, _quantity - 800) * _itemPrice * 0.15;
}
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 dicount(int inputVal, int quantity, int yearToDate){
if(inputVal > 50) inputVal-=10;
}
?
//重构后
int dicount(final int inputVal, int quantity, int yearToDate){
int result = inputVal;
if(result > 50) result-=10;
}
动机: 我想你很清楚“对参数赋值”这个说话的意思。如果把一个名称为foo的对象作为参数传递给某个函数,那么“对参数赋值”意味改变foo,使它引用另一个对象。但是,如果在“被传入对象”身上进行什么操作,那没问题,我们经常会这么做。这里只针对“fool被改变而指向另一个对象”这种情况来讨论:
void test(Object foo){
foo.changedBySomeWay(); //that's ok
foo=anotherObject; //trouble will appear
}
我们之所不这样做,是因为它降低了代码的清晰度,而且混用了按值传递和按引用传递这两种参数传递方式。JAVA只采用按值进行传递。
在按值传递的情况下,对参数的任何修改,都不会对调用端造成任何影响。如果你只以参数表示“被传递进来的东西”,那么代码会清晰的多,因为这种用法在所有语言中都表现出相同的语义。
在JAVA中,一般不要对参数赋值:如果你看到手上的代码已经这么做了,就应该使用本文的方法。