重构手法之重新组织函数【3】
5 Introduce Explaining Variable(引用解释性变量)
概要
你有一个复杂的表达式。
将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。
动机
有时候你会遇到一系列复杂的表达式连续运算的时候,这个时候你可能根本招架不住如此长或者是如此复杂的长函数。这个时候你可以通过引用临时变量来储存他们的结果,将这些长函数的结果分成一个个临时变量来让函数清晰化。这个重构手法在条件逻辑中用的比较多。
我们来看这样一个条件判断:
if(platform.ToUpper().IndexOf("MAC")>-1&&browser.ToUpper().IndexOf("IE")>-1&& wasInitialized()&&resize>0) { //do something }
是不是看上去晕晕的,不知道这个条件判断是判断什么的?
我们用这个手法重构一下:
bool isMacOs = platform.ToUpper().IndexOf("MAC") > -1; bool isIEBrowser = browser.ToUpper().IndexOf("IE") > -1; bool wasResized = resize > 0; if (isMacOs && isIEBrowser && wasInitialized()&& wasResized) { //do something }
这样代码是不是很清晰。
范例
我们从一个简单的计算开始:
double GetPrice() { return _quantity * _itemPrice - Math.Max(0, _quantity - 500) * _itemPrice * 0.05 + Math.Min(_quantity * _itemPrice * 0.1, 100.0); }
这段代码很简单,但是不好理解。可以进行Introduce Explaning Variable,将_quantity * _itemPrice的计算结果放进临时变量中。
double GetPrice() { double basePrice = _quantity * _itemPrice; return basePrice - Math.Max(0, _quantity - 500) * _itemPrice * 0.05 + Math.Min(basePrice * 0.1, 100.0); }
再将折扣提炼出来。
double GetPrice() { double basePrice = _quantity * _itemPrice; double quantityDiscount = Math.Max(0, _quantity - 500) * _itemPrice * 0.05; return basePrice - quantityDiscount + Math.Min(basePrice * 0.1, 100.0); }
最后把运费计算提炼出来。最终代码如下。
double GetPrice() { double basePrice = _quantity * _itemPrice; double quantityDiscount = Math.Max(0, _quantity - 500) * _itemPrice * 0.05; double shipping = Math.Min(basePrice * 0.1, 100.0); return basePrice - quantityDiscount + shipping; }
这里虽然完成了,但是我们前面讲过,临时变量只在它所处的那个函数中才有意义,局限性较大,函数则可以在对象的整个生命中都有用,并且可以被其他对象使用。所以下面我们使用Extract Method方法对刚刚的示例进行重构。同时我们也推荐使用这种方法。
运用Extract Method处理上述范例
同样这样一个函数:
double GetPrice() { return _quantity * _itemPrice - Math.Max(0, _quantity - 500) * _itemPrice * 0.05 + Math.Min(_quantity * _itemPrice * 0.1, 100.0); }
这一次我们把底价计算、批发折扣以及运费都提炼到一个新函数中。最终代码如下:
double GetPrice() { return GetBasePrice() - GetQuantityDiscount() + GetShipping(); } private double GetQuantityDiscount() { return Math.Max(0, _quantity - 500) * _itemPrice * 0.05; } private double GetBasePrice() { return _quantity * _itemPrice; } private double GetShipping() { return Math.Min(GetBasePrice() * 0.1, 100.0); }
比较两种手法
对比Extract Method和Introduce Explaining Variable这两种手法生成的函数:
1、前者生成的函数更短,更清晰易懂,而后者则产生了大量的临时变量,使函数变得更长;
2、前者生成了很多的独立函数,如果想在外部访问某个函数,直接调用就好,而后者得重新写方法供其调用。
小结
我个人比较推荐Extract Method这种手法,因为同一对象中的任何部分,都可以根据自己的需要取用这些提炼出来的函数。一开始我们可以把函数声明为private;如果其他对象需要它们,再释放这些函数的访问限制。
既然如此,那么我们什么时候使用Introduce Explaining Variable呢?在Extract Method这种手法需要花费更大工作量时。比如说,有一个拥用大量局部变量的算法,这时候用Extract Method这个手法就不好处理。
To Be Continued...