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

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

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

使用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. Java微笔记(7)

    String 类常用方法 注意点: 字符串 str 中字符的索引从0开始,范围为 0 到 str.length()-1 使用 indexOf 进行字符或字符串查找时,如果匹配返回位置索引:如果没有匹配 ...

  2. sed条件不修改匹配

    sed '/^echo/!s/text/subtext/g' 如果是以echo开始行首的行就不进行替换. 参考sed substitution conditional

  3. C语言文法阅读与理解序

    <指针>→*  | * < 指针> <直接声明符>  <标识符> | <直接声明>[]| <直接声明>[常量表达式] | < ...

  4. 201621123037 《Java程序设计》第10周学习总结

    作业10-异常 标签(空格分隔): Java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1. 常用异常 结合题集题目7 ...

  5. 201621123037《Java程序设计》第二周学习总结

    #Week02-Java基本语法与类库 1. 本周学习总结 关键词:常量池.对象.null.不可变性.string对象拼接.字符串池 关键概念之间的联系:Java中有常量池,超出常量池以外的就会新开辟 ...

  6. Linux的压缩/解压缩文件处理 zip & unzip

    Linux的压缩/解压缩命令详解及实例 压缩服务器上当前目录的内容为xxx.zip文件 zip -r xxx.zip ./* 解压zip文件到当前目录 unzip filename.zip 另:有些服 ...

  7. 第193天:js---Math+Error+Number+Object总结

    一.Math 随机选取 //随机选取 function getRandom (begin,end){ return Math.floor(Math.random()*(end-begin))+begi ...

  8. idea Class<>表示的含义

  9. BZOJ 2039 人员雇佣(最小割)

    最小割的建图模式一般是,先算出总收益,然后再通过网络模型进行割边减去部分权值. 然后我们需要思考什么才能带来收益,什么才能有权值冲突. s连向选的点,t连向不选的点,那么收益的减少量应该就是将s集和t ...

  10. [二十]SpringBoot 之 (多)文件上传

    (1)新建maven Java project 新建一个名称为spring-boot-fileuploadmaven java项目 (2)在pom.xml加入相应依赖: <project xml ...