返回總目錄 本小節目錄 Decompose Conditional(分解條件表達式) Consolidate Conditional Expression(合併條件表達式) 1Decompose Conditional(分解條件表達式) 概要 你有一個複雜的條件(if-else if-else)語句 ...
本小節目錄
1Decompose Conditional(分解條件表達式)
概要
你有一個複雜的條件(if-else if-else)語句。
從if、else if、else三個段落中分別提煉出獨立函數。
動機
複雜的條件邏輯往往會導致程式複雜度上升。編寫代碼來檢查不同的條件分支、根據不同的分支做不同的事往往又會導致函數過長。
將條件表達式分解為多個獨立函數,根據每個小塊代碼的用途,為分解而得的新函數命名,並將原函數中對應的代碼改為調用新建函數,從而更清楚地表達自己的意圖。對於條件邏輯,將每個分支條件分解成新函數還可以突出條件邏輯,更清楚地表明每個分支的作用,並且突出每個分支的原因。
範例
假設要計算購買某樣商品的總價,而這個商品在冬天和夏天的單價是不同的:
class Order { public double Quantity { get; set; } public double WinterRate { get; set; } public double SummerRate { get; set; } public double WinterServiceCharge { get; set; } public double GetCharge(DateTime date) { double result; if (date.Month < 3 || date.Month > 6) { result = Quantity * WinterRate + WinterServiceCharge; } else { result = Quantity * SummerRate; } return result; } }
現在把每個分支的判斷條件都提煉到一個獨立函數中:
class Order { public double Quantity { get; set; } public double WinterRate { get; set; } public double SummerRate { get; set; } public double WinterServiceCharge { get; set; } public double GetCharge(DateTime date) { double result; if (NotSummer(date)) { result = WinterCharge(Quantity); } else { result = SummerCharge(Quantity); } return result; } private bool NotSummer(DateTime date) { return date.Month < 3 || date.Month > 6; } private double SummerCharge(double quantity) { return quantity * SummerRate; } private double WinterCharge(double quantity) { return quantity * WinterRate + WinterServiceCharge; } }
通過這段代碼可看出整個重構帶來的清晰性。
小結
像上面這樣的情況,很多人都不會去提煉分支條件。因為這些分支條件往往非常短,看上去似乎沒有提煉的必要。但是,儘管這些條件往往很短,在代碼意圖和代碼自身之間往往存在不小的差距。就像上面那樣,NotSummer(date)這個語句比原本的代碼更好地表達自己的意圖。原來的代碼,我必須看看,想一想,才能說出其作用。當然了,這裡看起來似乎很簡單。即使是這樣,提煉出來的函數可讀性也更高一些。
2Consolidate Conditional Expression(合併條件表達式)
概要
你有一系列條件測試,都得到相同結果。
將這些測試合併為一個條件表達式,並將這個條件表達式提煉成為一個獨立函數。
動機
代碼里經常有這樣的檢查:檢查條件各不相同,最終行為卻一致。如果發現這種情況,應該使用“邏輯與”和“邏輯或”將它們合併為一個條件表達式。
之所以合併條件代碼,有兩個原因。(1)合併後的條件代碼會告訴你“實際上只有一次條件檢查,只不過有多個併列條件需要檢查而已”,從而使這一次檢查的用意更清晰。(2)這項重構往往是為了使用Extract Method做好準備。
範例:使用邏輯或
class Amount { public int Seniority { get; set; } public int MonthsDisabled { get; set; } public bool IsPartTime { get; set; } double DisablilityAmount() { if (Seniority < 2) { return 0; } if (MonthsDisabled > 12) { return 0; } if (IsPartTime) { return 0; } //compute the disability amount //your code here return 1; } }
這段代碼中,一連串的條件檢查都在做同一件事情。對於這樣的代碼,上述檢查等價於一個以邏輯或連接起來的語句:
class Amount { public int Seniority { get; set; } public int MonthsDisabled { get; set; } public bool IsPartTime { get; set; } double DisablilityAmount() { if (Seniority < 2 || MonthsDisabled > 12 || IsPartTime) { return 0; } //compute the disability amount //your code here return 1; } }
現在,我們觀察這個新的條件表達式,並運用Extract Method將它提煉成一個獨立函數,以函數名稱表達該語句所檢查的條件:
class Amount { public int Seniority { get; set; } public int MonthsDisabled { get; set; } public bool IsPartTime { get; set; } double DisablilityAmount() { if (IsNotEligibleForDisability()) { return 0; } //compute the disability amount //your code here return 1; } bool IsNotEligibleForDisability() { return Seniority < 2 || MonthsDisabled > 12 || IsPartTime; } }
範例:使用邏輯與
class Rate { public double GetRate() { if (OnVacation()) { if (LengthOfService() > 10) { return 1; } } return 0.5; } private bool OnVacation() { return true; } private int LengthOfService() { return 9; } }
這段代碼可以變成這樣:
class Rate { public double GetRate() { if (OnVacation() && LengthOfService() > 10) { return 1; } return 0.5; } private bool OnVacation() { return true; } private int LengthOfService() { return 9; } }
如果所觀察的部分只是對條件進行檢查並返回一個值,就可以使用三元操作符將這一部分變成一條return語句。因此,下列代碼:
if (OnVacation() && LengthOfService() > 10) { return 1; } return 0.5;
就變成了:
return (OnVacation() && LengthOfService() > 1) ? 1 : 0.5;
小結
那我們什麼時候不需要合併表達式呢?
即我們認為這些檢查的確彼此獨立,的確不應該被視為同一次檢查,那就不使用本項重構。
To Be Continued...