返回總目錄 本小節目錄 Move Method(搬移函數) Move Field(搬移欄位) 1Move Method(搬移函數) 概要 你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。 在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托 ...
本小節目錄
1Move Method(搬移函數)
概要
你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。
在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托函數,或是將舊函數完全移除。
動機
如果一個類有太多行為,或如果一個類與另一個類有太多合作而形成高度耦合,又或者使用另一個對象的次數比使用自己所駐對象的次數還多。那就要搬移函數。
搬移函數時,要根據“這個函數與哪個對象的交流比較多”來決定其移動路徑。
範例
用一個表示“賬戶”的Account類來說明這項重構:
class Account { private AccountType _accountType; private int _daysOverdrawn; /// <summary> /// 透支金額計費規則 /// </summary> /// <returns></returns> 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 {/// <summary> /// 透支金額計費規則 /// </summary> /// <returns></returns> 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……