對象調用某個函數,並將所得結果做為參數,傳遞給另一個函數。而接受參數的函數本身也能夠調用前一個函數。 讓參數接受者去除該項參數,並直接調用前一個函數。 ...
本小節目錄
- Replace Parameter with Methods(以函數取代參數)
- Introduce Parameter Object(引入參數對象)
- Remove Setting Method(移除設值函數)
8Replace Parameter with Methods(以函數取代參數)
概要
對象調用某個函數,並將所得結果做為參數,傳遞給另一個函數。而接受參數的函數本身也能夠調用前一個函數。
讓參數接受者去除該項參數,並直接調用前一個函數。
動機
如果函數通過其他途徑獲得參數值,那麼它就不應該通過參數取得該值。過長的參數列會增加程式閱讀者的理解難度,因此我們應該儘可能的縮短參數列的長度。
一種縮減參數列的辦法:看看“參數接受端”是否可以通過“與調用端相同的計算”來取得參數值。如果調用端通過其所屬對象內部的另一個函數來計算參數,併在計算過程中“未曾引用調用端的其他參數”,那麼就可以將這個計算過程轉移到被調用端內,從而去除該項參數。
範例
以下代碼用於計算定單折扣價格。
class Price { public int Quantity { get; set; } public int ItemPrice { get; set; } public double GetPrice() { int basePrice = Quantity * ItemPrice; int discountLevel = Quantity > 100 ? 2 : 1; double finalPrice = GetDiscountedPrice(basePrice, discountLevel); return finalPrice; } private double GetDiscountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) { return basePrice * 0.1; } return basePrice * 0.05; } }
首先,把計算折扣等級(discountLevel)的代碼提煉成一個獨立的函數:
private int GetDiscountLevel() { return Quantity > 100 ? 2 : 1; }
接下來替換所有的引用點,並使用Remove parameter去掉參數:
class Price { public int Quantity { get; set; } public int ItemPrice { get; set; } public double GetPrice() { int basePrice = Quantity * ItemPrice; double finalPrice = GetDiscountedPrice(basePrice); return finalPrice; } private int GetDiscountLevel() { return Quantity > 100 ? 2 : 1; } private double GetDiscountedPrice(int basePrice) { if (GetDiscountLevel() == 2) { return basePrice * 0.1; } return basePrice * 0.05; } }
使用相同方法,去除basePrice:
class Price { public int Quantity { get; set; } public int ItemPrice { get; set; } public double GetPrice() { return GetDiscountedPrice(); } private int GetBasePrice() { return Quantity * ItemPrice; } private int GetDiscountLevel() { return Quantity > 100 ? 2 : 1; } private double GetDiscountedPrice() { if (GetDiscountLevel() == 2) { return GetBasePrice() * 0.1; } return GetBasePrice() * 0.05; } }
最後觀察發現,可以對GetDiscountedPrice()函數使用Inline Method:
class Price { public int Quantity { get; set; } public int ItemPrice { get; set; } public double GetPrice() { if (GetDiscountLevel() == 2) { return GetBasePrice() * 0.1 } return GetBasePrice() * 0.05; } private int GetBasePrice() { return Quantity * ItemPrice; } private int GetDiscountLevel() { return Quantity > 100 ? 2 : 1; } }
小結
使用該重構手法可以縮減參數列,使程式更容易理解。
9Introduce Parameter Object(引入參數對象)
概要
某些參數總是很自然地同時出現。以一個對象取代這些參數。
動機
一組參數可能有幾個函數同時使用,這些函數可能隸屬於同一個類,也可能隸屬於不同的類。這樣的一組參數就是所謂的Data Clump(數據泥團),我們可以運用一個對象包裝所有這些數據,再以對象取代它們。本項重構的價值在於縮短參數列。此外,新對象所定義的訪問函數還可以使代碼更具一致性,這又進一步降低了代碼的理解和修改難度。
範例
/// <summary> /// 賬項類 /// </summary> class Entry { public DateTime ChargeDate { get; set; } public double Value { get; set; } public Entry(double value, DateTime chargeDate) { Value = value; ChargeDate = chargeDate; } } /// <summary> /// 賬目類 /// </summary> class Account { private List<Entry> _entries = new List<Entry>(); /// <summary> /// 計算兩個日期之間的賬項總量 /// </summary> /// <param name="start">開始日期</param> /// <param name="end">結束日期</param> /// <returns></returns> public double GetFlowBetween(DateTime start, DateTime end) { return _entries.Where(entry => entry.ChargeDate >= start && entry.ChargeDate <= end).Sum(entry => entry.Value); } }
現在客戶端要調用的話,可能代碼如下:
Account anAccount = new Account(); anAccount.GetFlowBetween(DateTime.Now, DateTime.Now.AddMonths(1));
我們現在以“範圍對象”來取而代之。首先在Account類新建一個簡單的數據容器,用以表示範圍:
class DateRange { public DateTime Start { get; } public DateTime End { get; } public DateRange(DateTime start, DateTime end) { Start = start; End = end; } }
然後修改GetFlowBetween()函數的參數:
/// <summary> /// 計算兩個日期之間的賬項總量 /// </summary> /// <param name="range"></param> /// <returns></returns> public double GetFlowBetween(DateRange range) { return _entries.Where(entry => entry.ChargeDate >= range.Start && entry.ChargeDate <= range.End).Sum(entry => entry.Value); }
現在客戶端調用的代碼可能變成這樣:
Account anAccount = new Account(); anAccount.GetFlowBetween(new DateRange(DateTime.Now, DateTime.Now.AddMonths(1)));
小結
本項重構還可以帶來更多的好處。當把這些參數組織到一起之後,往往很快可以發現可被移植新建類的行為。通常,將原本使用那些參數的函數對這一組參數會有一些共通的處理,如果將這些共同行為移動新對象中,可以減少重覆代碼。
10Remove Setting Method(移除設值函數)
概要
類中的某個欄位或者屬性應該在對象創建時被設值,然後就不再改變。
去掉該欄位或者屬性的所有設值函數。
動機
如果你為某個欄位或者屬性提供了設值函數,這就暗示了這個欄位或者屬性值可以被改變。如果你不希望在對象初創之後,此欄位或者屬性還有機會改變,那就不要為它提供設值函數。這樣你的意圖會更加清晰,並且可以排除其值被修改的可能性。
範例
看一個簡單的例子:
class Account { public string Id{ get; set; } public Account(string id) { this.Id ="ZZ"+id; } }
以上代碼可以修改為:
class Account { public string Id { get; } public Account(string id) { this.Id = "ZZ"+id; } }
To Be Continued……