您的位置:首页 > 其它

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

2016-10-07 22:41 573 查看
5. 引入解释性变量[b](Introduct Explaining Variable)[/b]

//引入解释性变量
//重构前
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐