第2章 重新组织函数:引入解释性变量分解临时变量和移除对参数的赋值
Posted 浅墨浓香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第2章 重新组织函数:引入解释性变量分解临时变量和移除对参数的赋值相关的知识,希望对你有一定的参考价值。
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章 重新组织函数:引入解释性变量分解临时变量和移除对参数的赋值的主要内容,如果未能解决你的问题,请参考以下文章