zoukankan      html  css  js  c++  java
  • 过长的函数---要重构的信号

    这个,我经常发现做开发的同事的代码,出现这个问题。
     
    "但是让小函数容易理解的真正关键在于一个好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。“
    -----------起个好名字,看名字知道函数的作用。在需要的时候,比如,调试,检查Bug,这时候,才需要查看。
     
     
     
    ”最终的效果是:你应该更积极地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们可以对一组甚至短短一行代码做这件事情。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离。“
     
    ----------按照这条原则写代码,分解函数。又学习了一条先进的经验,函数的名称要说明函数的用途。
     
     
    ”如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。如果你尝试运用Extract Method手法,最终就会把许多参数和临时变量当作参数,传递给被提炼出来的新函数,导致可读性几乎没有任何提升。此时,你可以经常运用Replace Temp With Query手法来消除这些临时元素。Introduce Parameter Object和Preserve Whole Object则可以将过长的参数列变得更简洁一些。“
     
     
     
     
    ---------------------使函数参数列表变短------------------------------------------------------------------------------------
    Introduce Parameter Object(引入参数对象)
    使用场景:一组参数总是被一起传递,作为好几个函数的参数。这一组参数,叫作数据泥团(Data Clumps)。这时就可以使用这个重构手法,缩短参数列表。长的参数列表,导致函数难以理解。
     
    实现的大概步骤:
    1.识别出上述的场景,一组参数总是被一起传递,作为函数的参数。
    2.创建一个新的类,类的名称描述这组参数的含义。它是一个不可变类。
    里面的成员都是final类型的,成员就是引入的参数。
    3.为新建类中的每个成员创建get方法。
    4.逐步使用新建类的成员替换函数参数。
     
    5.然后,检查函数中的操作,看哪些操作,可以提炼到参数对象中,作为参数对应的行为。
    可以提炼到参数对象中,作为参数对象的行为的操作,这一组操作具备如下特征:
    1).这组操作,使用到的参数都是参数对象中的成员。
    2).这组操作的用途可以用一个动作名称来表达。这个动作名称就是参数对象行为名称了。
     
    效果例子:
    getFlowBetween(Date start, Date end)-------Introduce Parameter Object手法---->getFlowBetween(Date range)。
     
     
     
     
    Preserve Whole Object(保持对象完整)
    该操作手法的含义:
    int low = daysTempRange()*getLow();
    int high = daysTempRange()*getHigh();
    withinPlan = plan.withinRange(low, high);
            |
            |应用Perserve Whole Object手法
            |
    withinPlan = plan.withinRange(daysTempRange());
     
    带来的好处:
    1.假设,某个时候,函数withinRange的需要的参数改变了,比如,需要使用某个对象中的三个参数,而不是某个对象中的两个参数。那么,如果之前没有使用该重构,那么,就要逐个修改该函数的被调用点。
    2.使函数的参数列表变短。
     
    缺点:
    “如果你传的是数值,被调用函数就只依赖于这些数值,而不依赖它们所属的对象。但如果你传递的是整个对象,被调用函数所在的对象就需要依赖参数对象。如果这会使你的依赖结构恶化,那么就不应该使用Preserve Whole Object手法”。
     
     
    其它方面的认识:
    如果被调用函数使用了来自另一个对象的很多项数据,这可能意味着该函数实际上应该被定义在那些数据所属的对象中。所以,考虑Preserve Whole Object的同时,你也应该考虑Move Method。”
     
    实现步骤:
    0.识别出哪些参数是属于某个对象,或者是可以统一作为某个对象的成员。
    1.如果函数参数,并不是属于某个对象的,则要使用Introduce Parameter Object手法,创建一个新的对象来包含该组参数。
     
    2.将引用该参数的地方,替换为引用新建对象的取值函数。
     
    通常做完之后,会发现,可以把被调用函数的操作,提炼为参数对象的行为,即参数对象的一个方法,函数。
    比如:
    class HeatingPlan:
    boolean withinRange(TempRange roomRange){
     
      return (roomRange.getLow() >= _range.getLow() && roomRange.getHigh() <= _range.getHigh());
     
    }
     
    -------->
    ---------------------使函数参数列表变短------------------------------------------------------------------------------------
     
     
    -------------------消除函数内的临时变量,从而使函数体变短-------------------------------------------------------
    Replace Temp With Query(以查询取代临时变量)
     
    看一个具体的例子:
    double getPrice(){
     
         int basePrice = _quantity * _itemPrice;
         double discountFactor;
         if ( basePrice > 1000 ) 
              discountFactor = 0.95;
         else  
             discountFactor = 0.98;
     
        return basePrice * discountFactor;
     
    }
    应用Replace Temp With Query(以查询取代临时变量)的手法,消除basePrice, discountFactor这两个临时变量
    效果如下:
    double getPrice(){
       return basePrice()*discountFactor();
    }
     
    实现过程:
    1.找出只被赋值一次的临时变量。
    --->如果某个临时变量被赋值超过一次,考虑使用Split Temporary Variable将它分割成多个变量。一个临时变量只应当承担一个责任,即是只应当被赋值一次,除了累加器变量和索引变量。
     
    2.将该临时变量声明为final。
     
    3.编译。
    这个确保该临时变量的确只被赋值一次。
     
    4.将”对该临时变量赋值“之语句的等号右侧部分提炼到一个独立函数中。
    --->首先将函数声明为private。日后你可能会发现有更多类需要使用它,那时放松对它的保护也很容易。
    --->确保提炼出来的函数无任何副作用,也就是说该函数并不修改任何对象内容。如果它有副作用,就对它进行Separate
    Query From Modifler手法。一个函数,要是是查询,也就是返回一个值;要么就是修改对象内容。不能让一个函数有两个功能,这会让函数代码难以阅读,容易出错。
     
    5.编译测试。
    6.在该临时变量身上使用Inline Temp手法。
     
    -------------------消除函数内的临时变量,从而使函数体变短-------------------------------------------------------  
     
     
    如何确定该提炼函数体中的哪一段代码?
     
    分两种信号:
    1.寻找注释。如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。
     
    2.函数题中的条件表达式和循环也是提炼的信号。要将循环提炼到一个独立函数中,这样可以使原有的函数体变小。对于函数体中的条件表达式,则使用Decompose Conditional手法来处理,目的也是使原有的函数体变小。
     
     
     
  • 相关阅读:
    转载:混淆包含SlidingMenu、gson等Android代码的proguard写法
    今天解决的两个问题
    C++中指针和引用的区别
    负载均衡服务器session共享的解决方案 (转载)
    Entity Framework的默认值BUG解决方法
    【转】SAPI中的IspeechRecoContext(接口)
    Sapi 添加语法的文章(转载)
    SAPI训练文件存储位置
    Flask第九篇 Flask 中的蓝图(BluePrint)
    Flask 第八篇 实例化Flask的参数 及 对app的配置
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4559164.html
Copyright © 2011-2022 走看看