Fork me on GitHub

重构手法之重新组织函数【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...

posted @ 2017-11-19 10:35  NaYoung  阅读(666)  评论(1编辑  收藏  举报