本小节目录
1Move Method(搬移函数)
概要
你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被或者调用。
在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除。
动机
如果一个类有太多行为,或如果一个类与另一个类有太多合作而形成高度耦合,又或者使用另一个对象的次数比使用自己所驻对象的次数还多。那就要搬移函数。
搬移函数时,要根据“这个函数与哪个对象的交流比较多”来决定其移动路径。
范例
用一个表示“账户”的Account类来说明这项重构:
class Account{ private AccountType _accountType; private int _daysOverdrawn; ////// 透支金额计费规则 /// ///double OverdraftCharge() { if (_accountType.IsPremium()) { double result = 10; if (_daysOverdrawn > 7) { result += (_daysOverdrawn - 7) * 0.85; } return result; } return _daysOverdrawn * 1.75; } double BankCharge() { double result = 4.5; if (_daysOverdrawn > 0) { result += OverdraftCharge(); } return result; }}
AccountType类如下:
class AccountType{ public bool IsPremium() { return true; } }
假设有几种新账户,每一种都有自己的“透支金额计费规则”。所有我们将OverdraftCharge()搬移到AccountType类去。
首先要做的就是:观察OverdraftCharge()使用的每一项特性,考虑是否值得将它们与OverdraftCharge()一起移动。此例中,我们需要让_daysOverdrawn 字段留在Account类中,因为这个值不会随着不同种类的账户而变化。然后我们将OverdraftCharge()函数代码复制到AccountType中,并做相应调整。
class AccountType{ public double OverdraftCharge(int daysOverdrawn) { if (IsPremium()) { double result = 10; if (daysOverdrawn > 7) { result += (daysOverdrawn - 7) * 0.85; } return result; } return daysOverdrawn * 1.75; } public bool IsPremium() { return true; }}
然后将源函数的函数本体替换为一个简单的委托动作。
class Account{ ////// 透支金额计费规则 /// ///double OverdraftCharge() { return _accountType.OverdraftCharge(_daysOverdrawn); }}
重构到这里就可以结束了。当然了,我们也可以删除Account中的源函数。我们找到源函数的所有调用者,并将这些调用重新定向,改为调用Account的BankCharge()。
class Account{ private AccountType _accountType; private int _daysOverdrawn; double BankCharge() { double result = 4.5; if (_daysOverdrawn > 0) { result += _accountType.OverdraftCharge(_daysOverdrawn); } return result; }}
此例中被搬移函数只引用了一个字段,所以只需将这个字段作为参数传给目标函数就行了。如果被搬移函数调用了Account中的另一个函数,可以将源对象传递给目标函数。
class AccountType{ public double OverdraftCharge(Account account) { if (IsPremium()) { double result = 10; if (daysOverdrawn > 7) { result += (account.GetDaysOverdrawn() - 7) * 0.85; } return result; } return account.GetDaysOverdrawn()* 1.75; } public bool IsPremium() { return true; }}
小结
在搬移函数时,检查源类中被源函数所使用的一切特性,考虑它们是否也该被搬移。如果某个特性只被你打算搬移的那个函数用到,那就应该将它一并搬移。如果另有其他函数使用了这个特性,就可以考虑将使用该特性的所有函数全都一并搬移。
2Move Field(搬移字段)
概要
在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。
在目标类建立一个字段,修改源字段的所有用户,令它们改用新字段。
动机
在类之间移动状态和行为,是重构中必不可少的措施。随着系统的发展,我们会发现自己需要新的类,并需要将现有的工作责任拖到新的类中。
对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它,就要考虑搬移这个字段。
范例
还是以Account类为例。
class Account{ private AccountType _accountType; private double _interestRate; double GetInterestForAmountByDays(double amount, int days) { return _interestRate * amount * days / 365; }}
我们想要把_interestRate搬移到AccountType类中去。目前已经有数个函数引用了它,GetInterestForAmountByDays()就是其中之一。
我们在AccountType中建立一个_interestRate字段,并封装成属性。
class AccountType{ private double _interestRate; public double InterestRate { get => _interestRate; set => _interestRate = value; }}
现在让Account类中访问的_interestRate字段的函数转而使用AccountType对象,并且删除Account类中的_interestRate字段。
class Account{ private AccountType _accountType; double GetInterestForAmountByDays(double amount, int days) { return _accountType.InterestRate * amount * days / 365; }}
小结
对于C#来说,可能这个重构手法叫“搬移属性”更合适一点。因为基本上字段都是私有的,属性才是供其他函数访问的。搬移属性做法和范例中是一样的。
To Be Continued……