有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method。重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷。但如果你遇到上种情况,你可能会天真的以为我只要适当的进行Replace Temp with Query,就可以把这种现象给化解。但情况往往事与愿违,不能达到你所理想的高度。这个时候你需要用到重构中的杀手锏--Replace Method with Method Object,这个手法出自Kent Beck [Beck]。
简单的说,这个重构会把源函数内的所有局部变量的参数都变成Method Object的字段。然后因为是字段,所以就不存在临时变量这个限制,你就可以轻轻松松的对新对象的新函数进行Extract Method将原来的大型函数进行轻松拆解。
做法:
- 建立一个新类,根据待处理函数的用途,为这个类起个好名字。
- 在新类中建立一个const字段,用来保存源函数所在的对象,我们将这个函数称为源对象。同时针对原函数的每个临时变量和参数,我们都在新类中增加相应的字段进行保存。
- 在新类中建立一个构造函数,其中构造函数的参数为原对象和原函数的参数(注意:字段里需要同时存储原函数的参数和临时变量,但是构造函数只需要原函数的参数)
- 在新类增加一个compute()函数
- 将原函数的代码复制到新对象的compute()函数中,然后把原函数中对原对象的调用都改成对新类中包含原对象的字段的调用。
- 编译
- 将旧函数的函数本体替换成创建上述新类的一个对象,然后对用它的compute()函数
完成这些之后,你就可以对新类中的compute()函数进行大刀阔斧的改造了。
例子:
class Account... int gamma(int inputVal, int quantity, int yearToDate)
{
int importantValue1 = (inputVal * quantity) + delta();
int importantValue2 = (inputVal * yearToDate) + ; if ((yearToDate - importantValue1) > )
importantValue2 -= ; int importantValue3 = importantValue2 * ; return importantValue3 - * importantValue1;
}
这是我们的原函数,可以看到,参数和临时变量非常多,非常难以进行Extract Method。所以是时候用出我们的杀手锏,首先按照『做法』,我们新建一个新类,然后将这个函数的参数和临时变量都变成他的字段。
class Gamma
{
private:
const Account *m_account;
int inputVal;
int quantity;
int yearToDate;
int importantValue1;
int importantValue2;
int importantValue3;
};
为了保证重构的小步进行,这里我们不对字段变量进行任何改名,这样有其何处,当你将原函数的实现搬过来的时候,你暂时不需要进行任何修改。
接下来我们进行构造函数的声明,注意:这个时候你只需要将原函数的所在对象和所需参数当作构造函数参数即可,不需要所有的临时变量。
public:
Gamma(Account *account, int inputValArg, int quantityArg, int yearToDateArg) :
m_account(account),
inputVal(inputValArg),
quantity(quantityArg),
yearToDate(yearToDateArg)
{
}
接下来,我们声明compute()函数,并且将原函数的实现搬过来,讲里面的声明删除,因为已经是新类的字段了,然后将对源对象本身的函数调用替换成对字段的调用,完整实现如下
class Gamma
{
public:
Gamma(Account *account, int inputValArg, int quantityArg, int yearToDateArg) :
m_account(account),
inputVal(inputValArg),
quantity(quantityArg),
yearToDate(yearToDateArg)
{
} int compute()
{
importantValue1 = (inputVal * quantity) + m_account->delta();
importantValue2 = (inputVal * yearToDate) + ; if ((yearToDate - importantValue1) > )
importantValue2 -= ; importantValue3 = importantValue2 * ; return importantValue3 - * importantValue1;
} private:
const Account *m_account;
int inputVal;
int quantity;
int yearToDate;
int importantValue1;
int importantValue2;
int importantValue3;
};
注意,其中的delta()函数,此时已经变成了m_account->delta(),并且所有变量的声明都已经被删除。
完成了这一步之后,其实我们已经结束了本次重构手法,但还没完,我们之所以运用Replace Method with Method Object就是为了更好的进行Extract Method,所以接下来,我们就可以轻轻松松对新类新函数进行重构,比如
class Gamma...
int compute()
{
importantValue1 = (inputVal * quantity) + m_account->delta();
importantValue2 = (inputVal * yearToDate) + ; importThing(); return importantValue3 - * importantValue1;
} private:
void importThing()
{
if ((yearToDate - importantValue1) > )
importantValue2 -= ;
}
这样,我们就轻松做到了对compute()函数的无参化的Extract Method,整个实现更加简洁,不需要去担心参数传递的问题。最后我们来看下最终被我们重构之后原函数的内容
class Account... int gamma(int inputVal, int quantity, int yearToDate)
{
return new Gamma(this, inputVal, quantity, yearToDate).compute();
}
原函数的实现变的异常简洁之外,我们可以对新类函数进行各种重构,最后就可以得到一个很好的重构效果。
『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object的更多相关文章
-
『重构--改善既有代码的设计』读书笔记----Replace Data Value with Object
当你在一个类中使用字段的时候,发现这个字段必须要和其他数据或者行为一起使用才有意义.你就应该考虑把这个数据项改成对象.在开发初期,我们对于新类中的字段往往会采取简单的基本类型形式来保存,但随着我们开发 ...
-
『重构--改善既有代码的设计』读书笔记----Replace Array with Object
如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...
-
『重构--改善既有代码的设计』读书笔记----Replace Temp with Query
Replace Temp with Query,顾名思义,表示你用查询来替换临时变量本身,临时变量对于函数来说是只有当前函数可见的,如果你在同类的别的地方要用到这个变量你就必须重新写表达式来获取这个变 ...
-
『重构--改善既有代码的设计』读书笔记----Extract Method
在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...
-
『重构--改善既有代码的设计』读书笔记----Change Value to Reference
有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...
-
『重构--改善既有代码的设计』读书笔记----Introduce Explaning Variable
有时候你会遇到一系列复杂的表达式连续运算的时候,这个时候你可能根本招架不住如此长或者是如此复杂的长函数.这个时候你可以通过引用临时变量来储存他们的结果,将这些长函数的结果分成一个个临时变量来让函数清晰 ...
-
『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data
当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...
-
『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field
如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙.也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访 ...
-
『重构--改善既有代码的设计』读书笔记----Move Method
明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...
随机推荐
-
Python执行系统命令的方法
Python中执行系统命令常见方法有两种: 两者均需 import os (1) os.system # 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 system(command) ...
-
VxWorks 6.9 内核编程指导之读书笔记 -- 多任务(二)
VxWorks的系统任务 VxWorks在引导时启动的系统任务依赖于配置,有些总是运行.任务集与VxWorks的基本配置相关,很少的任务常用于可选的组件. 注意:别挂起.删除或改变任何系统任务的优先级 ...
-
sass学习--什么是sass
1.预备知识--什么是 CSS 预处理器 CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这 ...
-
Android实现渐显按钮的左右滑动效果
本示例演示在Android中实现带渐显按钮的左右滑动效果. 关于滑动效果,在我的上一篇博文中提到过,有兴趣的朋友可以访问: http://www.cnblogs.com/hanyonglu/archi ...
-
JQuery和JS操作LocalStorage/SessionStorage的方法
LocalStorage 是对Cookie的优化 没有时间限制的数据存储 在隐私模式下不可读取 大小限制在500万字符左右,各个浏览器不一致 在所有同源窗口中都是共享的 本质是在读写文件,数据多的话会 ...
-
JLINK 10针J和20针JTAG接口连接方法
我的JLINK终于用上了,哈哈,好开心,终于不用考虑是不是要借用别人的PC机了,昨天到城隍庙电子市场忙活了一下午,终于算是满载而归,呵呵,好了,下面说一下接法,其实根本不需要什么转接板什么的,直接把相 ...
-
vue 结合 FileReader() 实现上传图片预览功能
项目中 身份证上传需求: <div class="ID_pic_wrap"> <ul> <li> <img src="../.. ...
-
JsonP的实现原理?
动态创建script标签,通过script标签中的src跨域属性,连接对方接口,并将回调函数通过接口传递给对方,对方服务器在准备好数据后再通过调用回调函数并以传递参数的方式将数据返回来.
-
Ajax的初步认识
1.背景 2005年,JJG发表了一篇在线文章,介绍了AJAX(Asynchronous Javascript + XML),这项技术能够向服务器请求额外数据而无须卸载页面,说是改变了以前的“单击”, ...
-
Kafka史上最详细原理总结
https://blog.csdn.net/ychenfeng/article/details/74980531 Kafka Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(pa ...