返回總目錄 本小節目錄 Pull Up Field(欄位上移) Pull Up Method(函數上移) Pull Up Constructor Body(構造函數本體上移) 1Pull Up Field(欄位上移) 概要 兩個子類擁有相同的欄位。將該欄位移至基類。 動機 如果各個子類是分別開發的, ...
本小節目錄
1Pull Up Field(欄位上移)
概要
兩個子類擁有相同的欄位。將該欄位移至基類。
動機
如果各個子類是分別開發的,或者是在重構過程中組合起來的,你常常會發現它們擁有重覆性,特別是欄位容易重覆。判斷若幹欄位是否重覆,唯一的辦法就是觀察函數如何使用它們。如果它們被使用的方式很相似,就可以將它們歸納到基類中去。
範例
如下代碼所示,Employee的兩個子類Salesman和Enginner都有_name欄位,所以可以考慮把這個欄位提到基類中。
class Employee { } class Salesman : Employee { private string _name; } class Enginner : Employee { private string _name; }
重構後的代碼如下,這樣提的前提是這些子類有一個基類或者有很多相似的欄位和方法,不然為了一個欄位而單獨建立一個抽象類是不可取的,所以這個就需要具體權衡。
class Employee { protected string _name; } class Salesman : Employee { } class Enginner : Employee { }
小結
本項重構主要是減少重覆:首先它去除了重覆的數據聲明;其次它使你可以將該欄位的行為從子類移至基類,從而去除重覆的行為。
2Pull Up Method(函數上移)
概要
有些函數,在各個子類中產生完全相同的結果。將該函數移至基類。
動機
避免行為重覆是很重要的。儘管重覆的兩個函數也可以各自工作得很好,但重覆自身只會成為錯誤的滋生地,此外別無價值。無論何時,只要系統中出現重覆,你就面臨“修改其中一個卻未能修改另一個”的風險。
使用本項重構的場合:(1)如果某個函數在各個子類中的函數體都相同;(2)子類的函數覆寫了基類的函數,但卻仍然做相同的工作。
範例
以Customer表示“顧客”,它有兩個子類:表示“普通顧客”的RegularCustomer和表示“貴賓”的PreferredCustomer。
public abstract class Customer { protected DateTime _lastBillDate; public void AddBill(DateTime date, double amount) { } } class RegularCustomer : Customer { void CreateBill(DateTime date) { double chargeAmount = ChargeFor(_lastBillDate, date); AddBill(date, chargeAmount); } public double ChargeFor(DateTime start, DateTime end) { return 0; } } class PreferredCustomer : Customer { void CreateBill(DateTime date) { double chargeAmount = ChargeFor(_lastBillDate, date); AddBill(date, chargeAmount); } public double ChargeFor(DateTime start, DateTime end) { return 100; } }
兩個子類中都有一個CreateBill()函數,並且代碼完全一樣,但我不能直接把這個函數上移到基類中,因為各個子類的ChargeFor()函數並不相同。必須先在基類中聲明一個ChargeFor()抽象函數:
public abstract class Customer { protected DateTime _lastBillDate; protected void AddBill(DateTime date, double amount) { } protected void CreateBill(DateTime date) { double chargeAmount = ChargeFor(_lastBillDate, date); AddBill(date, chargeAmount); } public abstract double ChargeFor(DateTime start, DateTime end); } class RegularCustomer : Customer { public override double ChargeFor(DateTime start, DateTime end) { return 0; } } class PreferredCustomer : Customer { public override double ChargeFor(DateTime start, DateTime end) { return 100; } }
小結
這個重構要根據具體情況使用,如果不是每個子類都有這個方法的話,可以考慮使用介面或者其他方式。
3Pull Up Constructor Body(構造函數本體上移)
概要
你在各個子類中擁有一些構造函數,它們的本體幾乎完全一致。
在基類中新建一個構造函數,併在子類構造函數中調用它。
動機
如果你看見各個子類中的函數有共同行為,第一個念頭應該是將共同行為提煉到一個獨立函數中,然後將這個函數提升到基類。對於構造函數而言,它們彼此的共同行為往往就是“對象的建構”。這時候你需要在基類中提供一個構造函數,然後讓子類都來調用它。
範例
class Employee { protected string _name; protected string _id; } class Manager:Employee { private int _grade; public Manager(string name,string id,int grade) { _name = name; _id = id; _grade = grade; } }
Employee的欄位應該在Employee構造函數中設值。因此定義了一個Employee構造函數,並將它聲明為Protected,表示子類應該調用它:
class Employee { protected string _name; protected string _id; protected Employee(string name, string id) { _name = name; _id = id; } } class Manager : Employee { private int _grade; public Manager(string name, string id, int grade) : base(name, id) { _grade = grade; } }
小結
這個重構手法和提升欄位、提升方法很相似。只不過是將“對象的建構”提升到基類中。
To Be Continued……