程序中,某个字段被其所驻类之外的另一个类更多地用到。在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段。
在类之间移动状态和行为,是重构过程中必不可少的措施。随着系统的发展,你会发现自己需要新的类,并需要将现有工作责任拖到新类中。
对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它,我们就可以考虑搬移这个字段。上述所谓“使用”可能是通过设值/取值函数间接进行的。我们也可能移动该字段的用户(某个函数),这取决于是否需要保持接口不受变化。如果函数看上去更适合待在原地,那么就搬移字段。
1. 如果字段的访问级是public,使用 Encapsulate Field将它封装起来。
如果有可能移动移动那些频繁访问该字段的函数,或如果有许多函数访问某个字段,先使用SelfEncapsulate Field也许会有帮助。
2.编译,测试
3.在目标类中建立与源字段相同的字段,并同时建立相应的设值/取值函数。
4. 编译目标类。
5. 决定如何在源对象中引用目标对象。
可能会有一个现成的字段或函数帮助取得目标对象。如果没有,就看能否轻松建立一个这样的函数。如果还是不行,就得在源类中新建一个字段来保存目标对象。这可能是一个永久性修改,但也可以让它是暂时的,因为后继的其他重构项目可能会把这个新建字段去掉。
6. 删除源字段。
7.将所有对元字段的引用替换为对某个目标函数的调用。
如果需要读取该变量,就把对源字段的引用替换为对目标取值函数的调用;如果要对该变量赋值,就把对源字段的引用替换成对设值函数的调用。
如果源字段不是private的,就必须在源类的所有子类中查找源字段的引用点,并进行相应替换。
8. 编译,测试。
下面是Account类的部分代码
public class Account { private AccountType type; private double interestRate; double interestForAmountDays( double amount, int days ) { return interestRate * amount * days / 365; } }我们把表示利率的interestRate搬移到AccountType类去。目前已有数个函数引用了它,interestForAmountDays() 就是其一。下一步我们要在AccountType中建立interestRate字段以及相应的访问函数:
public class AccountType { private double interestRate; public double getInterestRate() { return interestRate; } public void setInterestRate( double interestRate ) { this.interestRate = interestRate; } }这时候,可以编译新的AccountType类了。
现在我们需要让Account类中访问interestRate字段的函数转而是用AccountType对象,然后删除Account类中的interestRate字段。我们必须删除源字段,才能保证其访问函数的确改变了操作对象,编译器会帮我们指出未被正确修改的函数。
public class Account { private AccountType type; double interestForAmountDays( double amount, int days ) { return type.getInterestRate() * amount * days / 365; } }
如果有很多函数已经使用了interestRate字段,我应该先运用Self-Encapsulate Field(自我封装)
public class Account { private AccountType type; private double interestRate; double interestForAmountDays( double amount, int days ) { return getInterestRate() * amount * days / 365; } private void setInterestRate( double arg ) { interestRate = arg; } private double getInterestRate() { return interestRate; } }这样,在字段搬移之后,我就只需要修改访问函数;
public class Account { private AccountType type; double interestForAmountDays( double amount, int days ) { return getInterestRate() * amount * days / 365; } private void setInterestRate( double arg ) { type.setInterestRate( arg ); } private double getInterestRate() { return type.getInterestRate(); } }
以后若有必要,我们可以修改访问函数的用户,让它们使用新对象。Self-Encapsulate Field是的我们得以保持小步前进。如果我需要对类做许多处理,保持小步前进时有帮助的。特别值得一提的是:首先使用Self-Encapsulate Field是我们得以更轻松使用Move Method将函数搬移到目标类中。如果待搬移函数引用了字段的访问函数,那些引用点是无需修改的。