返回總目錄 本節包含3個手法: 1、Extract Method(提煉函數) 2、Inline Method(內聯函數) 3、Inline Temp(內聯臨時變數) Extract Method(提煉函數) 概要 你有一段代碼可以被組織在一起並獨立起來。 將這段代碼放進一個獨立函數中,並讓函數名稱解 ...
本節包含3個手法:
6.1Extract Method(提煉函數)
概要
你有一段代碼可以被組織在一起並獨立起來。
將這段代碼放進一個獨立函數中,並讓函數名稱解釋該函數的作用。
動機
如果有一個過長的函數或者需要一段註釋才能讓人理解用途的代碼,那麼就將這段代碼放進一個獨立函數中。
簡短而且命名良好的函數的好處:
- 函數粒度小,被覆用的機會大
- 使高層函數讀起來像註釋
- 函數都是細粒度,更易被覆寫
還記得在第二種壞味道——Long Method中說的函數到底有多長嗎?作者如是說:“在我看來,長度不是問題,關鍵在於函數名稱和函數本體之間的語義距離。如果提煉可以強化代碼的清晰度,那就去做,就算函數名稱比提煉出來的代碼還長也無所謂。”
範例
public class Receipt { private List<decimal> Discounts { get; set; } private List<decimal> ItemTotals { get; set; } public decimal CalculateGrandTotal() { decimal subTotal = 0m; //計算subTotal 的總和 foreach (decimal itemTotal in ItemTotals) { subTotal += itemTotal; } //subTotal 要迴圈減去discount,也就是計算Discount if (Discounts.Count > 0) { foreach (decimal discount in Discounts) { subTotal -= discount; } } //計算Tax decimal tax = subTotal * 0.065m; subTotal += tax; return subTotal; } }
重構後代碼如下:
public class Receipt { private List<decimal> Discounts { get; set; } private List<decimal> ItemTotals { get; set; } public decimal CalculateGrandTotal() { decimal subTotal = CalculateSubTotal(); subTotal = CalculateDiscounts(subTotal); subTotal = CalculateTax(subTotal); return subTotal; } //計算subTotal 的總和 private decimal CalculateSubTotal() { decimal subTotal = 0m; foreach (decimal itemTotal in ItemTotals) { subTotal += itemTotal; } return subTotal; } //計算折扣 private decimal CalculateDiscounts(decimal subTotal) { if (Discounts.Count > 0) { foreach (decimal discount in Discounts) { subTotal -= discount; } } return subTotal; } //計算Tax private decimal CalculateTax(decimal subTotal) { decimal tax = subTotal * 0.065m; subTotal += tax; return subTotal; } }
小結
這個重構手法是不是很簡單。我相信這個手法大多數人用的非常多,或許你潛意識裡就是這麼做的。
我不知道大家的公司有沒有在代碼編寫規範裡面把這個作為參考,比如一個方法最多不能超過多少行等等。這在一定程度上也能使程式員把這些複雜的邏輯剝離成意義很清楚的小方法。
6.2Inline Method(內聯函數)
概要
一個函數的本體與名稱同樣清楚易懂。
在函數調用點插入函數主體,然後移除該函數。
動機
使用間接層可以讓函數通俗易懂,但是如果這個函數本體本就通俗易懂,那這個間接層就是無用的間接層,可以將其去除。
範例
int GetRating() { return MoreThanFiveLateDeliveries() ? 2 : 1; } bool MoreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }
對於這個函數來說,MoreThanFiveLateDeliveries這個方法就是一個無用的間接層。因為原函數本就很清晰,此處將其去除。
重構後代碼如下:
int GetRating() { return _numberOfLateDeliveries > 5 ? 2 : 1; }
小結
這個手法是比較簡單的。但是不要因為簡單就不註意它。在內聯函數的時候,要確定該函數不具有多態性,因為子類無法覆寫一個根本不存在的函數。
6.3Inline Temp(內聯臨時變數)
概要
你有一個臨時變數,只被一個簡單表達式賦值一次,而它妨礙了其他重構手法。
將所有對該變數的引用動作,替換為對它賦值的那個表達式自身。
動機
這個手法多半是作為下一個要講的手法Replace Temp with Query的一部分來使用的。
單獨使用的情況是:某個臨時變數被賦予某個函數調用的返回值,而這個臨時變數妨礙了其他重構手法。
範例
bool GetBasePrice() { double basePrice = anOrder.GetBasePrice(); return basePrice > 1000; }
重構後代碼如下:
bool GetBasePrice() { return anOrder.GetBasePrice() > 1000; }
小結
在將臨時變數替換為表達式自身的時候要註意,這個臨時變數在該函數內是否只被賦值一次。如果不是一次,那麼就不要這麼做。
To Be Continued...