现在的位置: 首页 > 综合 > 正文

重新组织你的函数之四 :Replace Temp with Query(以查询取代临时变量)

2014年01月06日 ⁄ 综合 ⁄ 共 3277字 ⁄ 字号 评论关闭

你的程序以一个临时变量(temp)保存某一表达式的运算结果。

将这个表达式提炼到一个独立函数(译注:所谓查询式,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;

  }

动机(Motivation)

临时变量的问题在于:它们是暂时的,而且只能在所属函数内使用。由于临时变量只有在所属函数内才可见,所以它们会驱使你写出更长的函数,因为只有这样你才能访问到想要访问的临时变量。如果把临时变量替换为一个查询式(query method),那么同一个class中的所有函数都将可以获得这份信息。这将带给你极大帮助,使你能够为这个编写更清晰的代码。

Replace Temp with Query往往是你运用Extract Method 之前必不可少的一个步骤。局部变量会使代码难以被提炼,所以你应该尽可能把它们替换为查询式。

这个重构手法较为直率的情况就是:临时变量只被赋值一次,或者赋值给临时变量的表达式不受其他条件影响。其他情况比较棘手,但也有可能发生。你可能需要先运用 Split Temporary Variable 或Separate Query from Modifier 使情况变得简单一些,然后再替换临时变量。如果你想替换的临时变量是用来收集结果的(例如循环中的累加值),你就需要将某些程序逻辑(例如循环)拷贝到查询式(query method)去。

作法(Mechanics)

首先是简单情况:

· 找出只被赋值一次的临时变量。
Ø 如果某个临时变量被赋值超过一次,考虑使用Split Temporary Variable 将它分割成多个变量。

· 将该临时变量声明为final。
· 编译。
Ø 这可确保该临时变量的确只被赋值一次。
· 将「对该临时变量赋值」之语句的等号右侧部分提炼到一个独立函数中。
Ø 首先将函数声明为private。日后你可能会发现有更多class需要使用 它,彼时你可轻易放松对它的保护。
Ø 确保提炼出来的函数无任何连带影响(副作用),也就是说该函数并不修改任何对象内容。如果它有连带影响,就对它进行Separate Query from Modifier。

· 编译,测试。
· 在该临时变量身上实施Replace Temp with Query。

我们常常使用临时变量保存循环中的累加信息。在这种情况下,整个循环都可以被提为一个独立函数,这也使原本的函数可以少掉几行扰人的循环码。有时候,你可能会用单一循环累加好几个值,就像本书p.26的例子那样。这种情况下你应该针对每个累加值重复一遍循环,这样就可以将所有临时变量都替换为查询式(query)。当然,循环应该很简单,复制这些代码时才不会带来危险。

运用此手法,你可能会担心性能问题。和其他性能问题一样,我们现在不管它,因 为它十有八九根本不会造成任何影响。如果性能真的出了问题,你也可以在优化时期解决它。如果代码组织良好,那么你往往能够发现更有效的优化方案;如果你没有进行重构,好的优化方案就可能与你失之交臂。如果性能实在太糟糕,要把临时变量放回去也是很容易的。

范例(Example)

首先,我从一个简单函数开始:

   double getPrice() {

       int basePrice = _quantity * _itemPrice;

       double discountFactor;

       if (basePrice > 1000) discountFactor = 0.95;

       else discountFactor = 0.98;

       return basePrice * discountFactor;

   }

我希望将两个临时变量都替换掉。当然,每次一个。

尽管这里的代码十分清楚,我还是先把临时变量声明为final,检查他们是否的确只被赋值一次:

   double getPrice() {

      final int basePrice = _quantity * _itemPrice;

      final double discountFactor;

       if (basePrice > 1000) discountFactor = 0.95;

       else discountFactor = 0.98;

       return basePrice * discountFactor;

   }

这样一来,如果有任何问题,编译器就会警告我。之所以先做这件事,因为如果临时变量不只被赋值一次,我就不该进行该项重构。接下来我开始替换临时变量,每次一个。首先我把赋值(assignment)动作的右侧表达式提炼出来:

   double getPrice() {

       final int basePrice =
basePrice();

       final double discountFactor;

       if (basePrice > 1000) discountFactor = 0.95;

       else discountFactor = 0.98;

       return basePrice * discountFactor;

   }

   private int basePrice() {

   return _quantity * _itemPrice;

   }

编译并测试,然后开始使用Replace Temp with Query。首先把临时变量basePrice的第一个引用点替换掉:

   double getPrice() {

       final int basePrice = basePrice();

       final double discountFactor;

       if (basePrice() > 1000) discountFactor = 0.95;

       else discountFactor = 0.98;

       return basePrice * discountFactor;

   }

编译、测试、下一个(听起来像在指挥人们跳乡村舞蹈一样)。由于「下一个」已经是basePrice的最后一个引用点,所以我把basePrice临时变量的声明式一并摘除:

   double getPrice() {

       final double discountFactor;

       if (basePrice() > 1000) discountFactor = 0.95;

       else discountFactor = 0.98;

       return basePrice() * discountFactor;

   }

搞定basePrice之后,我再以类似办法提炼出一个discountFactor():

   double getPrice() {

       final double discountFactor =
discountFactor();

       return basePrice() * discountFactor;

   }

   private double discountFactor() {

       if (basePrice() > 1000) return 0.95;

       else return 0.98;

   }

你看,如果我没有把临时变量basePrice替换为一个查询式,将多么难以提炼discountFactor()!

最终,getPrice()变成了这样:

  double getPrice() {

       return basePrice() * discountFactor();

   }

抱歉!评论已关闭.