Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

重构技巧

2017-06-11
nivelle

提炼函数

一个过长的函数或者一段需要注释才能让人理解用途的代码,则将这段代码放进一个独立函数中。

void printOwing(double amount)
{
    printBanner();
    
    //print details
    System.out.println("name:"+_name);
    System.out.println("amount:"+_amount);
}


  ...

void pringOwing(double amount)
{
    printBanner();
    printDetails(amount);
}

void printDetails(double amount){
      System.out.println("name:"+_name);
      System.out.println("amount:"+_amount);
}


优点

  • 如果函数粒度很小,那么函数被复用的机会就更大
  • 使高层函数读起来就像一系列注释
  • 如果函数都是细粒度,那么函数的覆写也会更容易些

做法

  1. 创造一个新函数,根据这个函数的意图来对它命名(以他“做什么”来命名,而不是以他“怎样做”命名)

即使你想要提炼的代码非常简单,例如只是一条消息或者一个函数调用,只要新函数的名称能够以更好的方式昭示代码意图,你也应该提炼它。
但如果你想不出一个更有意义的名称,就别动

  1. 将提炼出的代码从原函数复制到新建目标函数中

  2. 检查源代码,看看是否其中引用了“作用于限于原函数”的变量(局部变量和源函数参数)。
  3. 检查是否有“仅用于被提炼代码段”的临时变量,如果有,在目标函数中将他们声明为临时变量。
  4. 检查被提炼代码段,查看是否有任何局部变量的值被它改变。如果一个临时变量的值被修改了,看看是否可以将被提炼代码段处理为一个查询,并将结果赋值给相关变量。
  5. 将被提炼代码中需要读取的局部变量,当做参数传递给目标函数。
  6. 在源函数中,将被提炼代码段替换为对目标函数的调用。

如果你将任何临时变量移到目标函数中,请检查他们原本的声明式是否在被提炼代码段的外围。

如果是,现在你可以删除这些声明式。


以查询取代临时变量

你的程序以一个临时变量保存某个表达式的运算结果,将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可以被其他函数引用。

double basePrice = _quantity * _itemPrice;
  if(basePrice > 1000)
  {
      return basePrice * 0.95;
  }else{
      return basePrice *0.98;
  }
  
  ...


if(basePrice()>1000){
    return basePrice() * 0.95;
}else{
    retuen basePrice() * 0.98;
}
...

double basePrice(){
    return _quality * _itemPrice;
}

临时变量的问题:

临时变量是暂时的,而且只能在所属函数内使用。由于临时变量只在所属函数内可见,所以它们会驱使你写更长的函数,因为只有这样你才能访问到需要的临时变量,如果把临时变量替换成一个查询,那么同一类中的所有函数都可以获得这份信息。

做法:

  1. 找出只被赋值一次的临时变量。
  2. 将该临时变量声明为final
  3. 编译。确保该临时变量只被赋值一次
  4. 将“对该临时变量赋值”之语句的等号右侧部分提炼到一个独立函数中。

    首先将函数声明为private.以后发现更多类需要它,放松对它的保护也很容易

    确保提炼出来的函数无任何副作用,也就是说该函数并不修改任何对象内容

  5. 在该变量身上实施内联临时变量。

内联临时变量

有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。

将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

double basePrice = anOrder.basePrice();
return (basePrice > 1000)

...

return (anOrder.basePrice()>1000)

动机:发现某个临时变量被赋予某个函数调用的返回值。一般来说这样的临时变量不会有任何危害,可以放心把它留在那儿。但如果这个临时变量妨碍了其他重构手法,就应该内联化

做法:

  1. 检查给临时变量的赋值语句,确保等号右边的表达式没有副作用
  2. 如果这个临时变量并未申明为final,那就将它声明为final,然后编译。

    这个以检查该临时变量是否真额只被赋值一次

  3. 找到该临时变量的所有引用点,将它们替换为“为临时变量赋值”的表达式。

评论