首页 > 代码库 > 第2章 重新组织函数(3):引入解释性变量、分解临时变量和移除对参数的赋值

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

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