我是靠谱客的博主 喜悦奇迹,最近开发中收集的这篇文章主要介绍重构:重新组织函数(《重构:改善既有代码的设计》第六章笔记),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、Extract Method(提炼函数)

如果你有一段代码可以被组织在一起并独立出来将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。

做法:

  • 创造一个新函数,根据这个函数的意图来给它命名(以它「做什么」来命名, 而不是以它「怎样做」命名)。
  • 将提炼出的代码从源函数(source)拷贝到新建的目标函数(target)中。
  • 仔细检查提炼出的代码,看看其中是否引用了「作用域(scope)限于源函数」的变量(包括局部变量和源函数参数)。
  • 检查是否有「仅用于被提炼码」的临时变量(temporary variables )。如果有,在目标函数中将它们声明为临时变量。
  • 检查被提炼码,看看是否有任何局部变量(local-scope variables )的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼码处理为一个查询(query),并将结果赋值给相关变量。
  • 将被提炼码中需要读取的局部变量,当作参数传给目标函数。
  • 处理完所有局部变量之后,进行编译。
  • 在源函数中,将被提炼码替换为「对目标函数的调用」。
  • 如果你将任何临时变量移到目标函数中,请检查它们原本的声明式是否在被提炼码的外围。如果是,现在你可以删除这些声明式了。
  • 编译,测试。

样例:

void printOwing(double previousAmount) {
    Enumeration e = _orders.elements();
    double outstanding = previousAmount * 1.2;
    // print banner
    System.out.println ("**************************");
    System.out.println ("***** Customer Owes ******");
    System.out.println ("**************************");
    // calculate outstanding
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
    //print details
    System.out.println ("name:" + _name);
    System.out.println ("amount" + outstanding);
}
​

 Extract Method====》

void printOwing(double previousAmount) {
    printBanner();
    double outstanding = getOutstanding(previousAmount * 1.2);
    printDetails(outstanding);
}
//对局部变量再赋值:
//如果这个变量在被提炼码之后未再被使用,只需直接在目标函数中修改它就可以了;
//如果被提炼码之后的代码还使用了这个变量,就需要让目标函数返回该变量改变后的值
double getOutstanding(double initialValue) {
    double result = initialValue;
    Enumeration e = _orders.elements();
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        result += each.getAmount();
    }
    //提炼码之后的代码还使用了这个变量,将变量返回
    return result;
}
//有局部变量,但不做修改,可以将它们当作参数传给目标函数
void printDetails (double outstanding) {
    System.out.println ("name:" + _name);
    System.out.println ("amount" + outstanding);
}
//无局部变量提炼
void printBanner() {
    // print banner
    System.out.println ("**************************");
    System.out.println ("***** Customer Owes ******");
    System.out.println ("**************************");
}

二、Inline Method(将函数内联化)

样例:

 int getRating() {
     return (moreThanFiveLateDeliveries()) ? 2 : 1;
 }
 boolean moreThanFiveLateDeliveries() {
     return _numberOfLateDeliveries > 5;
 }

Inline Method==》

 int getRating() {
     return (_numberOfLateDeliveries > 5) ? 2 : 1;
 }

三、Inline Temp(将临时变量内联化)

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

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

样例:

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

Inline Temp==》

return (anOrder.basePrice() > 1000)

四、Replace Temp with Query(以查询取代临时变量)

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

做法:将这个表达式提炼到一个独立函数中。将这个临时变量的所有「被引用点」替换为「对新函数的调用」。新函数可被其他函数使用。

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

Replace Temp with Query==》

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

五、Introduce Explaining Variable(引入解释性变量)

表达式非常复杂而难以阅读的情况下,临时变量可以帮助你将表达式分解为比较容易管理的形式。

double price() {
     // price is base price - quantity discount + shipping
     return _quantity * _itemPrice -
        Math.max(0, _quantity - 500) * _itemPrice * 0.05 +
        Math.min(_quantity * _itemPrice * 0.1, 100.0);
  }

Introduce Explaining Variable=>

double price() {
     final double basePrice = _quantity * _itemPrice;
     final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05;
     final double shipping = Math.min(basePrice * 0.1, 100.0);
     return basePrice - quantityDiscount + shipping;
}

Extract Method=>

   double price() {
       return basePrice() - quantityDiscount() + shipping();
   }
   private double quantityDiscount() {
       return Math.max(0, _quantity - 500) * _itemPrice * 0.05;
   }
   private double shipping() {
       return Math.min(basePrice() * 0.1, 100.0);
   }
   private double basePrice() {
       return _quantity * _itemPrice;
   }

什么情况下使用Introduce Explaining Variable ?

答案:在需要花费更大工作量去 Extract Method 时。如果要处理的是一个拥有大量局部变量的算法,那么使用 Extract Method 绝非易事。在这种情况下,先使用Introduce Explaining Variable 理清代码搞清楚代码逻辑之后,再运用 Replace Temp with Query 把被引入的那些解释性临时变量去掉。

六、Split Temporary Variable(剖解临时变量)

场景:有某个临时变量被赋值超过一次,但它既不是循环变量,也不是一个集用临时变量(collecting temporary variable)。

做法:针对每次赋值,创造一个独立的、对应的临时变量。

动机(Motivation)

 很多临时变量用于保存一段冗长代码的运算结果,以便稍后使用。这种临时变量应该只被赋值一次。如果它们被赋值超过一次,就意味它们在函数中承担了一个以上的责任。如果临时变量承担多个责任,它就应该被替换为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同的 事情,会令代码阅读者糊涂。

样例:

在这个例子中,acc变量有两个责任:第一是保存第一个力造成的初始加速度;第二是保存两个力共同造成的加速度。

 double getDistanceTravelled (int time) {
     double result;
    double acc = _primaryForce / _mass;        //译注:第一次赋值处
     int primaryTime = Math.min(time, _delay);
     result = 0.5 * acc * primaryTime * primaryTime;
     int secondaryTime = time - _delay;
     if (secondaryTime > 0) {
         double primaryVel = acc * _delay;        //以下是第二次赋值处
       acc = (_primaryForce + _secondaryForce) / _mass;        
         result +=  primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
     }
     return result;
 }

 Split Temporary Variable=>

   double getDistanceTravelled (int time) {
       double result;
     final   double primaryAcc  = _primaryForce / _mass;
       int primaryTime = Math.min(time, _delay);
       result = 0.5 * primaryAcc * primaryTime * primaryTime;
       int secondaryTime = time - _delay;
       if (secondaryTime > 0) {
           double primaryVel = primaryAcc * _delay;
          double acc = (_primaryForce + _secondaryForce) / _mass;
           result +=  primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;
       }
       return result;
   }

七、Substitute Algorithm(替换你的算法)

场景:把某个算法替换为另一个更清晰的算法。

做法:将函数本体(method body)替换为另一个算法。

   String foundPerson(String[] people){
       for (int i = 0; i < people.length; i++) {
           if (people[i].equals ("Don")){
               return "Don";
           }
           if (people[i].equals ("John")){
               return "John";
           }
           if (people[i].equals ("Kent")){
               return "Kent";
           }
       }
       return "";
   }

Substitute Algorithm=>

   String foundPerson(String[] people){
       List candidates = Arrays.asList(new String[] {"Don", "John", "Kent"});
       for (int i=0; i<people.length; i++)
           if (candidates.contains(people[i]))
               return people[i];
       return "";
   }

最后

以上就是喜悦奇迹为你收集整理的重构:重新组织函数(《重构:改善既有代码的设计》第六章笔记)的全部内容,希望文章能够帮你解决重构:重新组织函数(《重构:改善既有代码的设计》第六章笔记)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部