你的程序中,某个字段被其所驻类之外的另一个类更多的用到。在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段。

       动机:在类之间移动状态和行为,是重构过程中必不可少的措施。随着系统发展,你会发现自己需要新的类,并需要将现有的工作责任拖到新的类中。在这个星期看似合理而正确的设计决策,到了下个星期可能不再正确。这没问题,如果你从来没遇到这种情况,那才有问题。

如果发现对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它,就考虑搬移这个字段。上述所谓“使用”可能是通过设值/取值函数间接进行的。也可能移动该字段的用户(某个函数),这取决于是否需要保持接口不受变化。如果这些函数看上去很适合待在原地,就选择搬移字段。

使用Extract Class (提炼类)时,也可能需要搬移字段。此时可以先搬移字段,然后搬移函数。

做法:1、如果字段的访问级别是public,使用 Encapsulated Field (封装字段)将它们封装起来。如果你有可能移动那些频繁访问该字段的函数,或如果有许多函数访问某个字段,先使用 Self Encapsulate Field (自封装字段)也许会有帮助。

2、编译、测试。

3、在目标类中建立于源字段相同的字段,并同时建立相应的设值/取值函数。

4、编译目标类。

5、决定如何在源对象中引用目标对象。首先看是否有一个现成的字段或函数可以助你得到目标对象。如果没有,就看能否轻易建立这样一个函数。如果还不行,就得在源类中新建一个字段来存放目标对象。这可能是个永久性修改,但你也可以让它是暂时的。因为后续重构可能会把这个新建字段除掉。

6、删除源字段。

7、将所有对源字段的引用替换为某个目标函数的调用。如果需要读取该变量,就把对源字段的引用替换为对设值函数的调用。如果源字段不是private的,就必须在源类的所有子类中查找源字段的引用点,并进行相应替换。

8、编译、测试。

下面是Account class的部分代码:
class Account...
    private AccountType _type;
    private double _interestRate;
    double interestForAmount_days(double amount, int days) {
       return _interestRate * amount * days / 365;
    }

我想把表示利率的_interestRate搬移到AccountType class去。目前已有数个函数引用了它,interestForAmount_days()就是其一。下一步我要在AccountType中建立_interestRate field以及相应的访问函数:
class AccountType...
    private double _interestRate;

void setInterestRate(double arg) {
       _interestRate = arg;
    }
    double getInterestRate() {
       return _interestRate;
    }

这时候我可以编译新的 AccountType class。
现在,我需要让Account class中访问_interestRate field的函数转而使用AccountType对象,然后删除Account class中的_interestRate field。我必须删除source field,才能保证其访问函数的确改变了操作对象,因为编译器会帮我指出未正确获得修改的函数。

class Account...
    private double _interestRate;
    double interestForAmount_days(double amount, int days) {
       return _type.getInterestRate() * amount * days / 365;
    }

如果有很多函数已经使用了_interestRate field,我应该先运用Self Encapsulate Field(171):

class Account...
    private AccountType _type;
    private double _interestRate;
    double interestForAmount_days(double amount, int days) {
       return getInterestRate() * amount * days / 365;
    }
    private void setInterestRate(double arg) {
       _interestRate = arg;
    }
    private double getInterestRate() {
       return _interestRate;
    }

这样,在搬移field之后,我就只需要修改访问函数就行了:

double interestForAmount_days(double amount, int days) {
       return getInterestRate() * amount * days / 365;
    }
    private void setInterestRate(double arg) {
       _type.setInterestRate(arg);
    }
    private double getInterestRate() {
       return _type.getInterestRate();
    }

如果以后有必要,我可以修改访问函数(accessors)的用户,让它们使用新对象。Self Encapsulate Field(171)使我得以保持小步前进。如果我需要对class做许多处理,保持小步前进是有帮助的。特别值得一提的是:首先使用Self Encapsulate Field(171)使我得以更轻松使用Move Method(142)将函数搬移到target class中。如果待移函数引用了field的访问函数(accessors),那么那些引用点是无须修改的。

重构改善既有代码设计--重构手法11:Move Field (搬移字段)的更多相关文章

  1. 重构改善既有代码设计--重构手法13:Inline Class (将类内联化)

    某个类没有做太多事情.将这个类的所有特性搬移到另一个类中,然后移除原类. 动机:Inline Class (将类内联化)正好于Extract Class (提炼类)相反.如果一个类不再承担足够责任.不 ...

  2. 重构改善既有代码设计--重构手法12:Extract Class (提炼类)

    某个类做了应该由2个类做的事.建立一个新类,将相关的字段和函数从旧类搬移到新类. 动机:一个类应该是一个清楚地抽象,处理一些明确的责任.但是在实际工作中,类会不断成长扩展.你会在这儿加入一些功能,在哪 ...

  3. 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)

    重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...

  4. 重构改善既有代码设计--重构手法10:Move Method (搬移函数)

    你的程序中,有个函数与其所驻类之外的另一个类进行更多的交流:调用后者,或被后者调用.在该函数最常用引用的类中建立一个有着类似行为的新函数.将旧函数编程一个单纯的委托函数,或是将旧函数完全移除. 动机: ...

  5. 重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型 ...

  6. 重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)

    代码对一个 参数赋值.以一个临时变量取代该参数的位置.     int Discount(int inputVal, int quantity, int yearTodate) { if (input ...

  7. 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)

      发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().in ...

  8. 重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)

    所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. ...

  9. 重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)

    Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLa ...

随机推荐

  1. MacBook Pro 15寸常见问题及修复

    苹果MacBook Pro更换SSD硬盘攻略教程 MacBook pro开机黑屏解决 苹果电脑 MAC PRO 开机黑屏了 MacBook Pro 开机后黑屏,怎么办啊 如果 Mac 无法开机 Mac ...

  2. Traffic Steering for Service Function Chaining

    Introduction 目前通过vlan标签来把流量引向对应的sfc 以前的sfc静态(SFs相邻组成SFC),有了sdn之后具有动态性.(SFs不需要彼此相邻.将流量动态地导向所需的SFs.) 流 ...

  3. Eclipse 如何安装反编译插件

    安装反编译插件 1.Help——Eclipse Marketplace 2.输入 Decompiler 搜索并安装此插件 3.根据提示无脑下一步,安装好,重启后(如果还是无法编译,需要把默认打开cla ...

  4. 小程序获取 openid 和 session_key

    <?php //获取openid function getopenid(){//获取用户ID //code为前端通过 wx.login() 方式获取 $code = $_GET["co ...

  5. linux svn启动和关闭

    linux svn启动和关闭 博客分类: linux系统 svnlinux  1,启动SVN sudo svnserve -d -r /home/data/svn/ 其中 -d 表示守护进程, -r ...

  6. MyEclipse+SSH开发环境配置

    MyEclipse+Struts+Hibernate+Mysql开发环境配置 软件: jdk-6u22-windows-x64.exe apache-tomcat-6.0.29.exe mysql-5 ...

  7. Linux服务器启动后只读解决办法

    今天处理一个服务器,远程死活连接不上,只好跑信息中心去看了下服务器. Linux服务器启动之后,提示: give root password for maintenance (or type cont ...

  8. javascriptDOM编程

    DOM - Document Object Model,它是W3C国际组织的一套Web标准,它定义了访问HTML文档对象的一套属性,方法和事件. <html> <head> & ...

  9. postman 上一个接口返回值传给下一个接口

    问题:如何将A请求responseBody中的token传入B请求中的request中 把A请求中的token设置为环境变量,如下: tests["Status code is 200&qu ...

  10. day 008 文件操作

    08. 万恶之源-⽂文件操作本节主要内容:1. 初识⽂文件操作2. 只读(r, rb)3. 只写(w, wb)4. 追加(a, ab)5. r+读写6. w+写读7. a+写读(追加写读)8. 其他操 ...