重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)
你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method。
将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型函数分解为数个小型函数。
class Order...
double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation;
...
}
动机:局部变量的存在会增加函数分解的难度。如果一个函数之中局部变量泛滥,那么想分解这个函数是非常困难的。Replace Temp with Query (以查询取代临时变量)可以帮助你减轻这一负担,但有时候你会发现根本无法拆解一个需要拆解的函数。这种情况下,应该使用函数对象。
做法:1、建立一个新类,根据待处理函数的用途,为这个类命名。
2、在新类中建立一个const字段,用以保存原来大型函数所在的对象。我们将这个字段称为“源对象”。同时,针对原函数的每个临时变量和每个参数,在新类中建立一个对应的字段保存之。
3、在新类中建立一个构造函数,接收源对象及原函数的所有参数。
4、在新类中建立一个compute()函数。
5、将原函数的代码复制到compute()函数中。如果需要调用源对象的任何函数,请通过源对象字段调用。
6、编译。
7、将旧函数的函数本体替换为这样一条语句“创建上述新类的一个新对象,而后调用其中的compute()函数”。
由于所有局部变量现在都成了字段,所以你可以任意分解这个大型函数,不必传递任何参数。
范例(Example)
如果要给这一重构手法找个合适例子,需要很长的篇幅。所以我以一个不需要长篇幅(那也就是说可能不十分完美)的例子展示这项重构。请不要问这个函数的逻辑是什么,这完全是我且战且走的产品。
Class Account
int gamma (int inputVal, int quantity, int yearToDate) {
int importantValue1 = (inputVal * quantity) + delta();
int importantValue2 = (inputVal * yearToDate) + 100;
if ((yearToDate - importantValue1) > 100)
importantValue2 -= 20;
int importantValue3 = importantValue2 * 7;
// and so on.
return importantValue3 - 2 * importantValue1;
}
为了把这个函数变成一个函数对象(method object),我首先需要声明一个新class。在此新class中我应该提供一个final值域用以保存原先对象(源对象);对于函数的每一个参数和每一个临时变量,也以一个个值域逐一保存。
class Gamma...
private final Account _account;
private int inputVal;
private int quantity;
private int yearToDate;
private int importantValue1;
private int importantValue2;
private int importantValue3;
按惯例,我通常会以下划线作为值域名称的前缀。但为了保持小步前进,我暂时先保留这些值域的原名。
接下来,加入一个构造函数:
Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) {
_account = source;
inputVal = inputValArg;
quantity = quantityArg;
yearToDate = yearToDateArg;
}
现在可以把原本的函数搬到compute()了。函数中任何调用Account class的地方,我都必须改而使用_account值域:
int compute () {
importantValue1 = (inputVal * quantity) +_account.delta();
importantValue2 = (inputVal * yearToDate) + 100;
if ((yearToDate - importantValue1) > 100)
importantValue2 -= 20;
int importantValue3 = importantValue2 * 7;
// and so on.
return importantValue3 - 2 * importantValue1;
}
然后,我修改旧函数,让它将它的工作转发〔委托,delegate)给刚完成的这个函 数对象(method object):
int gamma (int inputVal, int quantity, int yearToDate) {
return new Gamma(this, inputVal, quantity, yearToDate).compute();
}
这就是本项重构的基本原则。它带来的好处是:现在我可以轻松地对compute()函数采取 Extract Method,不必担心引数(argument)传递。
int compute () {
importantValue1 = (inputVal * quantity) + _account.delta();
importantValue2 = (inputVal * yearToDate) + 100;
importantThing();
int importantValue3 = importantValue2 * 7;
// and so on.
return importantValue3 - 2 * importantValue1;
}
void importantThing() {
if ((yearToDate - importantValue1) > 100)
importantValue2 -= 20;
}
重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)的更多相关文章
- 重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)
Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLa ...
- 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)
重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...
- 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)
发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().in ...
- 重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)
所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. ...
- 重构改善既有代码设计--重构手法01:Extract Method (提炼函数)
背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner() ...
- 重构改善既有代码设计--重构手法10:Move Method (搬移函数)
你的程序中,有个函数与其所驻类之外的另一个类进行更多的交流:调用后者,或被后者调用.在该函数最常用引用的类中建立一个有着类似行为的新函数.将旧函数编程一个单纯的委托函数,或是将旧函数完全移除. 动机: ...
- 重构改善既有代码设计--重构手法11:Move Field (搬移字段)
你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段. 动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...
- 重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)
代码对一个 参数赋值.以一个临时变量取代该参数的位置. int Discount(int inputVal, int quantity, int yearTodate) { if (input ...
- 重构改善既有代码设计--重构手法19:Replace Data Value with Object (以对象取代数据值)
你有一笔数据项(data item),需要额外的数据和行为. 将这笔数据项变成一个对象. class Order... private string customer; ==> class Or ...
随机推荐
- python __call__ 函数
__call__ Python中有一个有趣的语法,只要定义类型的时候,实现__call__函数,这个类型就成为可调用的. 换句话说,我们可以把这个类型的对象当作函数来使用,相当于 重载了括号运算符. ...
- EXT4+Struts2 JSON的问题
ERROR : Class org.apache.struts2.json.JSONWriter can not access a member of class org.springframewor ...
- c语言学习—图书搜索
请问下:你说的C四大圣经指那几本啊?——<C 陷阱与缺陷> && <C程序设计语言> && <C专家编程> && & ...
- lintcode395-硬币排成线 II
395-硬币排成线 II 有 n 个不同价值的硬币排成一条线.两个参赛者轮流从左边依次拿走 1 或 2 个硬币,直到没有硬币为止.计算两个人分别拿到的硬币总价值,价值高的人获胜. 请判定 第一个玩家 ...
- [二叉查找树] 1115. Counting Nodes in a BST (30)
1115. Counting Nodes in a BST (30) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...
- 团队项目利用Msbuild自定义Task实现增量发布
最近一直在做自动部署工具,主要利用到了Msbuild的自定义Task,通过Task我们可以自定义编译.部署过程减少人工直接干预.Msbuild的详细用法,可以去园子里搜一下,有很多的基础教程,这里就不 ...
- 2nd 四人小组项目的进一步分析
组长:林莉 组员:王东涵.宫丽君.胡丽娜 项目选题:车辆管理系统(附加相关员工管理) 项目期限:暂定十周 一.NABCD模型 N-Need 需求分析及相应功能设置 需求概述: 管理库中车辆信息.相关人 ...
- jquery中on绑定click事件在苹果手机失效问题解决(巨坑啊)
描述:用一个div写一个按钮,并给这个按钮添加一个点击事件,在安卓机器上一切正常,但是在苹果机型上会出现点击事件失效. <!DOCTYPE html> <html lang=&quo ...
- 理解promise 01
原文地址: http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html 用Javascript的小伙伴们,是时候承认了,关于 ...
- python OCR 图形识别
1.pip install pyocr 2.pip install PIL 3.安装tesseract-ocr http://jaist.dl.sourceforge.net/project/tess ...