zoukankan      html  css  js  c++  java
  • 第2章 重新组织函数(3):引入解释性变量、分解临时变量和移除对参数的赋值

    5. 引入解释性变量(Introduct Explaining Variable)

    //引入解释性变量
    //重构前
    if((platform.toUpperCase().indexOf("MAC") > -1) &&
       (browser.toUpperCase().indexOf("IE") > -1) &&
        wasInitialized() && resize > 0)
    {
        //do something
    }
    
    //重构后
    const bool isMacOs     = platform.toUpperCase().indexOf("MAC") > -1;
    const bool isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
    const bool wasResized = resize > 0; 
    
    if (isMacOs && isIEBrowser && wasInitialized() && wasResized())
    {
        //do something
    }

    5.1 动机

    (1)将复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式的用途

    (2)引入临时变量可以帮助将表达式分解为比较容易管理的形式。如条件逻辑中,可以将每个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。

    5.2 做法

    (1)声明一个const临时变量,将待分解的复杂表达式中的一部分动作的运算结果赋值给它。

    (2)将表达式中的“运算结果”这一部分,替换为上述临时变量

    (3)编译,测试。

    (4)重复上述过程,处理表达式的其他部分。

    5.3 范例

    //重构前(其中的_quantity和_itemPrice为类的成员变量)
    double price()
    {
        //价格 = 底价(basePrice) - 批发折扣(quantity discount) + 运费(shipping)
        //底价 = 数量(quantity) * 单价(itemPrice) 
        return  _quantity * _itemPrice -  //底价
                Math.max(0, _quantity - 500) * _itemPrice * 0.05 +
                Math.min(_quantity * _itemPrice * 0.1, 100.0);            
    }
    
    //重构中(引入解释性变量)
    double price()
    {
        const double basePrice = _quantity * _itemPrice;
        const double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05;
        const double shipping = Math.min(basePrice * 0.1, 100.0);
        
        return basePrice - quantityDiscount + shipping;
    }
    
    //运用ExtractMethod方法对上述范例进行重构(去除上述的解释性变量,即临时变量)
    double price()
    {
        return basePrice() - quantityDiscount() + shipping();
    }
    
    //底价
    double basePrice()
    {
        return _quantity * _itemPrice;
    }
    //批发折扣
    double quantityDiscount()
    {
        return Math.max(0, _quantity - 500) * _itemPrice * 0.05;
    }
    
    //运费
    double shipping()
    {
        return Math.min(basePrice * 0.1, 100.0);
    }

    5.4 思考

    (1)当要处理一个拥有大量局部变量的算法时,使用ExtractMethod比较困难。这种情况下可以使用Introduce Explaining Variable来理解代码。

    (2)搞清代码逻辑后,总可以运用Replace Temp with Query把中间引入的那些解释性临时变量去掉。如果使用Replace Method with Method Object,那么中间引入的那些解释性临时变量也可以作为成员变量,即体现其用途所在。

    6. 分解临时变量

    //分解临时变量
    //重构前
    double temp = 2 * (_height + _width);  //周长
    cout << temp << endl;
    
    temp = _height * _width; //面积
    cout << temp << endl;
    
    //重构后
    const double perimeter = 2 * (_height + _width);  //周长
    cout << perimeter << endl;
    
    const double area = _height * _width; //面积
    cout << area << endl;

    6.1 动机

    代码中如果某个临时变量被赋值超过一次(循环变量除外),就意味着在该函数中它承担了一个以上的职责。这时可以分解个多个临时变量,每个变量只承担一个责任

    6.2 做法

    (1)在待分解临时变量的声明及其第1次被赋值处,修改其名称

    (2)将上述新的临时变量声明为const变量

    (3)以该临时变量的第2次赋值动作为界,修改此前对该临时变量的所有引用,让它们引用新的临时变量。

    (4)在第2次赋值处,重新声明原先那个临时变量。

    (5)编译测试,重复上述过程。每个都在声明处对该临时变量改名,并修改下次赋值之前的引用点。

    6.3 范例

    //分解临时变量
    //场景:根据牛顿第二定律计算物理从静止开始运动,在
    //指定时间内的运动距离(分两个阶段)
    //第1阶段:在一个力的作用下。第2个阶段在两个力的共同作用下)
    double getDistanceTravelled(int time)
    {
        double result = 0.0;
        double acc = _primaryForce / _mass; //F = ma;注意acc被赋值两次。
        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;
    }
    
    //重构中(对第1次赋值重构)
    double getDistanceTravelled(int time)
    {
        double result = 0.0;
        const double primaryAcc = _primaryForce / _mass; //F = ma;注意acc被赋值两次。
        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;
    }
    //重构后
    double getDistanceTravelled(int time)
    {
        double result = 0.0;
        const double primaryAcc = _primaryForce / _mass; //F = ma;注意acc被赋值两次。
        int primaryTime = Math.min(time, _delay);
        result = 0.5 * primaryAcc * primaryTime * primaryTime;
        
        int secondaryTime = time - _delay;
        if(secondaryTime > 0)
        {
            double primaryVel = primaryAcc * _delay; //初始速
            const double secondaryAcc = (_primaryForce + _secondaryForce) / _mass;
            
            result += primaryVel * secondaryTime +
                      0.5 * secondaryAcc * secondaryTime * secondaryTime;
        }
        
        return result;
    }

    7. 移除对参数的赋值(Remove Assignments to Parameters)

    7.1 动机

    (1)注意“对参数赋值”的意思如果把一个名为foo的对象指针作为参数传给某个函数,那么“对参数赋值”意味改变foo,使它指向了另一个对象。如果是在“被传入对象”上调用某个函数而改变对象内部的状态,这不叫“对参数赋值”。如

    void aMethod(Object* foo)
    {
        foo->modifyInSomeWay(); //没问题
        foo = anotherObject;    //对参数赋值!
    }

    (2)注意传值和传地址的区别。按值传递的情况下,对参数的任何修改,不会对调用端造成影响。

    (3)对面那些使用“出参数”的语言,不必遵循这条规则。

    (4)为了防止对参数赋值,也可以将其声明为const类型,如上例void aMethdo(Object* const foo);

    7.2 做法

    (1)建立一个临时变量,把待处理的参数值赋予它。

    (2)以“对参数的赋值”为界,将所有其后对此参数的引用点,全部替换为“对此临时变量的引用”

    (3)修改赋值语句,使其改为对新建之临时变量赋值。

    7.3 范例

    //对参数赋值
    //重构前(注意第1个参数为引用类型)
    int discount (int& inputVal, int quantity, int yearToDate)
    {
        if(inputVal > 50) inputVal = -2;
        if(quantity > 100) inputVal = -1;
        if(yearToDate > 10000) inputVal = -4;
        return inputVal;
    }
    
    //重构后
    int discount (int& inputVal, int quantity, int yearToDate)
    {
        int result = inputVal;
        if(inputVal > 50) result = -2;
        if(quantity > 100) result = -1;
        if(yearToDate > 10000) result = -4;
        return result;
    }
  • 相关阅读:
    Retrofit 2使用要点梳理:小白进阶回忆录
    浅析Android动画(三),自定义Interpolator与TypeEvaluator
    浅析Android动画(二),属性动画高级实例探究
    浅析Android动画(一),View动画高级实例探究
    Retrofit 2使用要点梳理:浅析POST文件/表单上传
    AS历史代码记录,找回本地未提交代码
    注解基础篇:自定义Java Annotation
    注解提高篇:自定义注解处理器(APT)
    AndroidStudio快捷键大全
    VisualStudio2015常用快捷键
  • 原文地址:https://www.cnblogs.com/5iedu/p/5937003.html
Copyright © 2011-2022 走看看