『重构--改善既有代码的设计』读书笔记----Move Method
明确函数所在类的位置是很重要的。这样可以避免你的类与别的类有太多耦合。也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁。简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和别的类进行交互,调用后者或者被后者调用,那么你就要注意了,你要去判断这个类是否真正适合他原来所在的类。
简单来说,这套手法就是在该函数最常引用的新类中建立一个有着类似行为的新函数,让旧函数变成一个单纯的委托函数或者完全删掉。
Move Method是重构理论的支柱。如果一个类的责任太多,或者一个类和别的类有太多合作,耦合太高,都需要考虑Move Method让类变得简单点。你需要多浏览你自己写的类,观察类的职责是否太多,与别的类的耦合程度问题。当然,如果你进行了Move Field,你也应该做这样的检查,因为我们都知道,函数就跟字段有关,字段都被你移动了,函数肯定要再检查一遍(如果同时需要Move Field和Move Method,一般先进行Move Filed会比较简单)。一旦发现了这样的函数,你要去分析他的调用端和被调用端,如果存在多态,你也应该去考虑继承体系中他所重定义的函数,来判断最终的移动路径。
如果你不能肯定是否该移动一个函数,你就暂且别去考虑移动这个函数,你可以转而去分析其他函数,移动其他函数往往会比你做这个决定来的简单点。如果你发现你移动了其他函数之后你仍然无法判定是否要移动这个函数,那么显然此时的这个函数移动与不移动已经不重要了。
做法:
- 检查源类中被源函数所使用的一切特性(字段和函数),考虑它们是否应该也一起搬移。如果某个特性(函数或者字段)只被你要移动的那个函数单独用到,你可以一起搬移走。如果你发现在源类中其他函数也使用了这个特性,你应该考虑将使用这些特性的所有函数一并搬移走,有时候移动一组函数比一个一个搬移函数来的简单。
- 检查源类的子类和超类,看看是否有该函数的其他声明。如果有其他声明,说明这个函数是呈多态性的,除非你的目标类也呈现相同的继承体系,不然你可能无法搬走。
- 在目标类声明这个函数,接口名称可以一样也可以不一样,由你根据语境把握。
- 将源函数的代码复制到目标函数中,调整这些代码,比如如果目标函数使用了源类中的特性(字段和函数),你就得做出决定如何让目标函数获取到源对象,如果目标类没有相应的引用机制,你应该考虑是否增加参数表,把源对象直接传过去。如果你此时使用了异常,你也应该考虑异常应该放在源类中还是放在目标类中去。
- 编译目标类。
- 决定如何从源函数去引用你新建的目标函数,如果存在一个现成的字段或者函数可以让你拿到目标对象那肯定最好,如果没有,考虑是否可以建立一个这样的取得目标类对象的函数,如果还是比较困难,你就该考虑是否应该给源类新建一个字段用来专门保存目标类对象,别去担心这会不会是一个永久性修改,因为后期的重构项目可能会让你把这些字段给删除掉。
- 修改源函数,让他变成一个单纯的委托函数去调用目标函数。
- 编译,测试。
- 如果你经常在源类中引用目标函数,那么你保留源函数接口让他变成一个单纯的委托函数显然更容易一些。
- 如果你要删除源函数,请将源类中对源函数的调用都替换为对目标函数的调用。你可以每修改一个点就编译测试一次,你也可以利用通过你的编辑器进行批量修改然后进行编译测试,这可能会更简单点。
- 编译,测试。
例子:
class Account...
double overdraftCharge()
{
if (m_type.isPremium())
{
double result = ; if (m_daysOverdrawn > )
{
result += (m_daysOverdrawn - ) * 0.85;
return result;
}
else
{
return m_daysOverdrawn * 1.75;
}
}
} double bankCharge()
{
double result = 4.5;
if (m_daysOverdrawn > )
{
result += overdraftCharge();
return result;
}
} private:
AccountType m_type;
int m_daysOverdrawn;
我们发现会根据type的不同进行相应的不同运算规则,这提示我们应该把函数放到变化的类中去,即应该放到AccoountType中去。首先,我们观察搬移函数,观察其中使用的源类特性,发现只有m_daysOverdrawn是源类的特性,但我们不选择将他搬移到目标类中而是放在源类中,因为我们可以断定,他不会随着账户的种类变化而变化,注意:这个技巧还是很重要的,将不变化的东西放到固定的类中,将容易变化的东西移动到相应的变化类中,可以让我们后期重构做更好的多态处理。对于这次重构很简单,我们可以直接以传参的方式传给目标函数。接下来,我们在AccountType中新建函数,并且命名,在这里我们可以选择同样的名称,然后把函数代码全部贴过去,然后做相应的改变
class AccountType...
double overdraftCharge(int daysOverdrawn)
{
if (isPremium())
{
double result = ; if (daysOverdrawn > )
{
result += (daysOverdrawn - ) * 0.85;
return result;
}
else
{
return daysOverdrawn * 1.75;
}
}
}
把属于自己的isPremium()变成直接调用,以参数的形式替代源类中自己的字段。当我们需要源类的特性的时候其实有四种途径
- 将这个特性也搬移到目标类中
- 建立或使用一个从目标类到源类的引用
- 将源对象当作参数传给目标函数
- 如果所需特性只是个变量,将它作为参数传给目标函数
在这次例子中我们用了第四种方式,只是简单的传递变量参数给目标函数。调整目标函数使之通过编译之后我们进行源函数的修改,让他变成一个简单的委托,因为我们在源类中已经存在的AccountType的字段,所以改起来很简单,改完之后进行编译测试。
class Account...
double overdraftCharge()
{
return m_type.overdraftCharge(m_daysOverdrawn);
}
我们可以保留源函数现在这样的样子也可以删除,如果你要删除你就要多做一步,就是找到源函数的所有调用者,然后进行替换,比如Account中的bankCharge()函数用到了这个源函数就进行相应的替换,替换完成之后你就可以删除源函数的声明了。
double bankCharge()
{
double result = 4.5;
if (m_daysOverdrawn > )
{
result += m_type.overdraftCharge(m_daysOverdrawn);
return result;
}
}
如果被搬移的函数不是private,你就必须检查是否有其他类也使用了这个函数,因为C++是强类型语言,你可以简单的删除源函数的声明,让编译器来帮你进行查找。
因为在这个例子之中之用了源类的一个字段,所以我们可以简单的以传参的形式给目标函数,如果源函数中使用了源类的别的函数或者多个字段,我们就必须传源类对象给目标函数
class AccountType...
double overdraftCharge(Account *account)
{
if (isPremium())
{
double result = ; if (account->daysOverdrawn() > )
{
result += (account->daysOverdrawn() - ) * 0.85;
return result;
}
else
{
return account->daysOverdrawn() * 1.75;
}
}
}
比如我们要获取daysOverdrawn必须要通过源类的别的函数或者需要源类的多个特性,那么此时我们就必须传源类对象过去让目标函数中进行调用。如果目标函数中需要源类的太多特性,那么你就得进一步进行重构。通常,你需要Extract Method并将其中一部分移回源类。
『重构--改善既有代码的设计』读书笔记----Move Method的更多相关文章
- 『重构--改善既有代码的设计』读书笔记----Extract Method
在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...
- 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object
有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...
- 『重构--改善既有代码的设计』读书笔记----Move Field
在类与类之间搬移状态和行为,是重构过程中必不可少的步骤.很有可能在你现在觉得正常的类,等你到了下个礼拜你就会觉得不合适.或者你在下个礼拜创建了一个新的类并且你需要讲现在类的部分字段和行为移动到这个新类 ...
- 『重构--改善既有代码的设计』读书笔记----Inline Method
加入间接层确实是可以带来便利,但过多的间接层有时候会让我自己都觉得有点恐怖,有些时候,语句本身已经够清晰的同时就没必要再嵌一个函数来调用了,这样只会适得其反.比如 void test() { if ( ...
- 『重构--改善既有代码的设计』读书笔记----Replace Array with Object
如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...
- 『重构--改善既有代码的设计』读书笔记----Introduce Foreign Method
当你无法获得一个类的源代码或者没有权限去修改这个类的时候,你对于这种为你服务的类,你可能会出现需要别的需求的时候,比如一个Date类,你需要能够让他本身直接返回出他的后一天的对象,但他没有,这个时候你 ...
- 『重构--改善既有代码的设计』读书笔记----Extract Class
在面向对象中,对于类这个概念我们应该有一个清晰的责任认识,就是每个类应该只有一个变化点,每个类的变化应该只受到单一的因素,即每个类应该只有一个明确的责任.当然了,说时容易做时难,很多人可能都会和我一样 ...
- 『重构--改善既有代码的设计』读书笔记----Introduce Local Extension
同Introduce Foreign Method一样,很多时候你不能修改编辑原始类,你需要为这些服务类增加一些额外的函数,但你没有这个权限或者入口.如果你只需要一个或者两个外加函数那么你可以放心的使 ...
- 『重构--改善既有代码的设计』读书笔记----Inline Class
如果某个类没有做太多的事情,你可以将这个类的所有特性搬移到另外一个类中,然后删除原类.可以看到,Inline Class正好和Extract Class相反,后者是将一个巨类分解成多个小类从而来分担责 ...
随机推荐
- java String 去除空格
1. java 去掉字符串的空格(中间空格,左右空格) 比如 时间字符串,去掉‘-’,‘:’,与空格 String x = "2008-09-08 11:12:23"; x=x.r ...
- word中MathType公式不能 二次编辑解决方案
问题:当新建文档然后插入公式,此时可以利用mathtype进行编辑,保存后推出第二次打开,双击公式却发现不能编辑公式. 解决方案: ////////////////////////////////// ...
- iOS 多线程学习笔记 —— NSOperation
本文复制.参考自文章:iOS多线程编程之NSOperation和NSOperationQueue的使用 ,主要为了加强个人对知识的理解和记忆,不做他用.原作者声明: 著作权声明:本文由http://b ...
- jQuery、实例大全
文章出处 http://www.cnblogs.com/suoning/p/5683047.html 一.简介 定义 jQuery创始人是美国John Resig,是优秀的Javascript框架: ...
- JavaScript高级程序设计15.pdf
组合继承的问题是会调用2次超类型构造函数 寄生组合式继承 即通过借用构造函数来继承属性,通过原型链的形式来继承方法,思路:不必为了指定子类型的原型而调用超类型的原型,我们所需要的无非是超类型原型的一个 ...
- Eclipse中svn图标不显示
在菜单栏中:windows ->preferences->General->Appearance->Lable Decorations 勾选其中的 SVN 项,最后应用确认之后 ...
- debian添加sudo
debian安装好默认没有sudo,都要su到root,感觉很容易手抖打错命令. 于是通过 apt-get install sudo安装sudo 然后就是添加有权限的用户到 /etc/sudoers ...
- 一个awk命令的demo
/prefix_* | awk -F'\x3' '{print $2}' | awk -F'\x2' '{for(i=0; i<NF; i++)print $i}'> ~/20140819 ...
- SIP协议错误代码大全
100 Trying 说明caller正在呼叫,但还没联系上callee. 180 Ringing 说明callee已经被联系上,callee的铃正在响.收到这个信息后,等待200 OK 181 Ca ...
- runtime/KVO等面试题
整理中... 1.KVO内部实现原则 回答:1>KVO是基于runtime机制实现的 2>当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中 ...