在类与类之间搬移状态和行为,是重构过程中必不可少的步骤。很有可能在你现在觉得正常的类,等你到了下个礼拜你就会觉得不合适。或者你在下个礼拜创建了一个新的类并且你需要讲现在类的部分字段和行为移动到这个新类中。如果你发现在一个类中的某个字段,更多的被别的类的函数所使用,包括设值set和取值get函数锁取用,那么你就应该考虑搬移这个字段。当然,你也可能会去考虑是否使用Move Method去搬移这些使用这个字段的函数到这个字段的源类中去,这取决于你是否能够接受接口的变化,如果这些函数更加适合待在原地不动,那么你就应该选择Move Field。如果你此时需要将行为和字段都搬移出去出去形成一个新类(Extract Class),那么此时你应该先Move Field之后再Move Method。

做法:

  • 如果你需要搬移的字段是public级别的,你需要使用Encapsulate Field先将它封装起来。如果你可能需要移动那些频繁访问该字段的函数,或者有很多函数访问某个字段,那么Self Encapsulate Field可能对你有帮助。
  • 编译,测试。
  • 在目标类建立和源类相同的字段,并建立相应的设值和取值函数。
  • 编译目标类。
  • 决定如何在源对象中引用目标对象。首先查看是否有现成的字段或者函数可以取得目标对象,如果没有就尝试能不能建立一个这样的函数,如果还是不能,就考虑在源类新建一个字段来存放目标对象,这可能是永久性的修改,但也可能因为后续的重构你会把这个新建字段给删除。
  • 删除源字段。
  • 将所有对源字段的引用替换为对某个目标函数的调用。如果需要读取变量,就把对源字段的引用替换为对目标取值函数的调用,如果需要对变量赋值,就把对源字段的引用替换为对目标设值函数的调用。如果源字段不是private级别的,就必须在源类的所有子类中查找替换对源字段的引用,并进行替换。
  • 编译,测试。

例子:

class Account...
private:
AccountType m_type;
double m_interestRate;
public:
double interestForAmountdays(double amount, int days)
{
return m_interestRate * amount * days / ;
}

我想把m_interestRate移到AccountType中去,我们可以看到,interestForAmountdays这个函数已经引用了这个字段,接下来我们要做的就是在AccountType中建立同样的字段,并设置取值和设值函数

class AccountType...
private:
double m_interestRate;
public:
void setInterestRate(double arg)
{
m_interestRate = arg;
}
double interestRate() const
{
return m_interestRate;
}

添加完成后,我们对目标类进行编译。现在我们需要将源类中对原字段访问的函数转而使用对目标取值函数的调用,然后我们删除源类中的字段。删除源字段也可以让编译器帮我们查找出所有需要修改的函数。

double interesetForAmountdays(double amount, int days)
{
return m_type.interestRate() * amount * days / ;
}

这里演示的是单一函数存在对源字段的引用。如果这里有很多函数都对原字段进行过引用,我们可以先使用Self Encapsulate Field进行自我封装。

class Account...
private:
AccountType m_type;
double m_interestRate;
public:
double interesetForAmountdays(double amount, int days)
{
return interestRate() * amount * days / ;
} void setInterestRate(double arg)
{
m_interestRate = arg;
} double interestRate() const
{
return m_interestRate;
}

这个带来的好处就是当我们搬移字段中之后,我们不需要去追踪所有引用字段的函数,我们只需要去修改取值函数和设值函数就可以达到所有函数的修改。

double interesetForAmountdays(double amount, int days)
{
return interestRate() * amount * days / ;
} void setInterestRate(double arg)
{
m_type.setInterestRate(arg);
}
double interestRate() const
{
return m_type.interestRate();
}

当然,如果你也可以让这些调用访问函数的用户直接去访问目标对象。Self Encapsulate Field可以让你的重构进行小步伐前进,很有帮助。小步伐前进也是重构能够稳步前进的秘诀,可以让你的工作和生活变得更加轻松。特别值得一提的是,如果你一开始就使用Self Encapsulate Field,如果此时你需要使用Move Method,在这个函数中可能正好有对该字段的访问

double interesetForAmountdays(double amount, int days)
{
return interestRate() * amount * days / ;
}

他在Account类中,被你搬移到了AccountType中,因为你没有在函数里写成字段,而是写的取值函数,这意味着你这段函数即时到了AccountType中你也无需修改,你只需要对AccountType增加对应的取值函数interestRate()即可完成Move Method。

『重构--改善既有代码的设计』读书笔记----Move Field的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记----Move Method

    明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...

  2. 『重构--改善既有代码的设计』读书笔记----Extract Method

    在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...

  3. 『重构--改善既有代码的设计』读书笔记----Extract Class

    在面向对象中,对于类这个概念我们应该有一个清晰的责任认识,就是每个类应该只有一个变化点,每个类的变化应该只受到单一的因素,即每个类应该只有一个明确的责任.当然了,说时容易做时难,很多人可能都会和我一样 ...

  4. 『重构--改善既有代码的设计』读书笔记----Inline Class

    如果某个类没有做太多的事情,你可以将这个类的所有特性搬移到另外一个类中,然后删除原类.可以看到,Inline Class正好和Extract Class相反,后者是将一个巨类分解成多个小类从而来分担责 ...

  5. 『重构--改善既有代码的设计』读书笔记----Change Value to Reference

    有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...

  6. 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object

    有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...

  7. 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data

    当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...

  8. 『重构--改善既有代码的设计』读书笔记----Replace Array with Object

    如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...

  9. 『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field

    如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙.也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访 ...

随机推荐

  1. oracle查询语句2【转载】

    本文使用的实例表结构与表的数据如下: scott.emp员工表结构如下:   SQL> DESC SCOTT.EMP; Name     Type         Nullable Defaul ...

  2. 网络编程——TCP协议的三次握手和四次挥手

    三次握手原理解析 TCP握手协议在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND ...

  3. HDU 1502 Regular Words DP+高精度

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1502 题目大意:找出总的满足条件的字符串数,num(a)=num(b)=num(c)且任何前缀均满足n ...

  4. Top 10 Java Debugging Tips with Eclipse

    In this tutorial we will see about debugging java applications using Eclipse. Debugging helps us to ...

  5. 3D视频的质量评价报告 (MSU出品)

    俄罗斯的MSU Graphics & Media Lab (Video Group)出品的3D视频的质量评价报告.测试了一些3D视频的质量,其测试方法值得我们参考.在此翻译一下部分文字. 注: ...

  6. ural 1106. Two Teams 二分图染色

    链接:http://acm.timus.ru/problem.aspx?space=1&num=1106 描述:有n(n<=100)个人,每个人有一个或多个朋友(朋友关系是相互的).将其 ...

  7. sqlserver 遇到以零作除数错误的处理 不报错的解决方法

    使用sqlserver 的选项来禁止出现以零除的错误中断,让而让其为null set ansi_warnings offSET ARITHABORT offSET ARITHIGNORE on sel ...

  8. JAVA 线程状态以及synchronized,wait,sleep,yield,notify,notifyAll

    java线程存在以下几种状态: 1: 创建状态(New):线程被new出来,还未调用start 2: 就绪状态(Runnable):又称为可执行状态,调用线程的start方法后,线程处于就绪状态,,线 ...

  9. Python脚本运行出现语法错误:IndentationError: unindent does not match any outer indentation level(转)

    [问题] 一个python脚本,本来都运行好好的,然后写了几行代码,而且也都确保每行都对齐了,但是运行的时候,却出现语法错误: IndentationError: unindent does not ...

  10. [AngularJS] Sane, scalable Angular apps are tricky, but not impossible.

    Read fromhttps://medium.com/@bluepnume/sane-scalable-angular-apps-are-tricky-but-not-impossible-less ...