`

何时提炼函数 & 用查询取代临时变量

 
阅读更多

拥有[短函数」(short methods)的对象会活得比较好、比较长。不熟悉面向对象技术的人,常常觉得对象程序中只有无穷无尽的delegation(委托),根本没有进行任何计算。

  

我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们可以对一组或甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数「做什么」和「如何做」之间的语义距离。

   

如何确定该提炼哪一段代码昵? 一个很好的技巧是:寻找注释。它们通常是指出「代码用途和实现手法间的语义距离」的信号。如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。

 

以查询取代临时变量  

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

 

方法步骤:

1:找出只被赋值一次的临时变量(如果某个临时变量被赋值超过一次,考虑用split temporary variable将它分割)

2:将临时变量声明为final。(确保变量只是被赋值一次)

3:编译。

4:将对该临时变量赋值的语句等号右侧部分提炼到一个独立函数中。(声明为private,确保这个函数不修改任何的对象内容)

5:在该临时变量身上实施用查询取代临时变量。

  

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

   

Example

1   原来代码

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; 
} 
  

 

这样一来,如果有任何问题,编译器就会警告我

  

下面一步步来:

首先把basePrice右边的提取到一个函数,然后if里面的引用这个变量的点,改为调用这个函数:

 

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; 
} 

 

 

最后的return语句是对于这个变量的最后一个引用点,所以把临时变量移除,然后改为调用方法:

double getPrice() { 
    final double discountFactor; 
    
    if (basePrice() > 1000) {
        discountFactor = 0.95; 
    } else {
        discountFactor = 0.98; 
    }
    
    return basePrice() * discountFactor; 
} 

    

同理提炼discountFactor,最后函数变为:

 

double getPrice() { 
    return basePrice() * discountFactor(); 
} 
 

 

非常简练了了!!

 

 

19
4
分享到:
评论
7 楼 pywepe 2012-02-19  
  楼主的文章拷贝自 重构

重构一书很强调提炼函数,不过出经常提到物必反的深刻道理,还得自己权衡.
6 楼 五点晨曦 2012-02-18  
函数嵌套得太多一样很麻烦的,我也不喜欢一句话函数。我提取函数的标准是某段代码在两个或以上的地方出现过
5 楼 zui4yi1 2012-02-17  
面向过程有面向过程的优点,面向对象有面向对象的优点。

把参数都提出来,相当于面向对象的思想。虽然我是搞JAVA的,不过我不喜欢一句话的函数,起名字都要累坏。
4 楼 greatghoul 2012-02-17  
greatghoul 写道
觉得没有提炼之前更容易理解一些吧。

不过你介绍的一些方法还是值得一试的
3 楼 greatghoul 2012-02-17  
觉得没有提炼之前更容易理解一些吧。
2 楼 hubeen 2012-02-17  
没看出这样做有什么意义。太多方法调用反而影响可读性和运行速度。求指教
1 楼 j2ee_dll 2012-02-16  
nice,受教了!

相关推荐

Global site tag (gtag.js) - Google Analytics