條件邏輯有可能十分複雜,因此本章提供一些重構手法,專門用來簡化它們。其中一項核心重構就是Decompose Conditional,可將一個複雜的條件邏輯分成若干小塊。這項重構很重要,因為它使邏輯和操作細節的分離。
本章的其餘重構手法可用以處理另一些重要問題,如果你發現代碼中的多處測試有相同的結構,應該實施Consolidate Conditional Expression,如果條件代碼中有任何重複,可以運用Consolidate Duplicate Conditional Fragment 將重複成分去掉。
較之於過程化程式而言,物件導向因為多態的機制可以簡化條件行為的細節
1. Replace Nested Condition with Guard Clauses(以衛語句
取代嵌套條件運算式)
Clauses[klɔ:zis] :字句 Guard[ga:d]: 守衛、看守 Nesting: 嵌套
範例:
想象一個薪酬系統,其中以特區規則處理死亡、駐外、退休員工的薪資,這些情況不常有,但偶然會出現
常規編碼:
1: private double getPlayAmount(){
2: double result;
3: if(_isDead){
4: result = deadAmount();
5: }else{
6: if(_isSeparated){
7: result = separatedAmount();
8: }else {
9: if(_isRetired){
10: result = retiredAmount();
11: }else {
12: result = normalAmount();
13: }
14: }
15: }
16: return result;
17: }
分析: 條件運算式通常有兩種表現形式。第一種:所有的分支都屬於正常行為,應該用
if..else表達出來。第二種:條件運算式提供的答案中只有一種是正常行為,其
他都是不常見的行為。此時就要單獨檢查該條件,並在該條件為真時立刻從函數
中返回,這種單獨檢測常常為稱為“衛語句”通過分析題意,我們知道該函數主
體是付普通情況的工資,而死亡、駐外、退休屬於特區情況,因此我們把他們做
為衛語句處理,修改後為:
1: private double getPlayAmount(){
2:
3: if(_isDead){
4: return deadAmount();
5: }
6:
7: if(_isSeparated){
8: return separatedAmount();
9: }
10:
11: if(_isRetired){
12: return retiredAmount();
13: }
14:
15: return normalAmount();
16: }
2. Decompose Conditional (分解條件運算式)
decompose [ˌdi:kəmˈpəuz] :分解、腐爛
-----你有一個複雜的條件陳述式(if、then、else),從if ,then,else三個段落中分別提煉出獨立函數
假設我要計算購買某樣商品的總價(總價=數量 * 單價),而這個商品在冬季和夏季的單價是不同的。
if(data.before(SUMMER_START) || data.after(SUMMER_END)){ charge = quantity * _winterRate ; }else{ charge = quantity * _summerRate }
分析:複雜的條件邏輯是最常導致複雜度上升的原因之一,常見的做法是1) 將if段提煉出來,構成一個獨立的函數 2)將then和else段提煉出來構成一個獨立的函數
修改後的代碼為:
if(notSummer(data)){ charge = winterCharge(quantity); }else{ charge = summerCharge(quantity); } private boolean notSummer(Data data){ return data.before(SUMMER_START) || data.after(SUMMER_END); } private double summerCharge(int quantity){ return quantity * _summerRate; } private double winterCharge(int quantity){ return quantity * _winterRate; }
通過以上的簡化,條件運算式邏輯塊就像代碼注釋一樣清晰、明了
3. Consolidate Conditional Expression (合并條件運算式)
----你有一系列的條件測試,都得到相同的結果,將這些測試合并為一個條件運算式,並將這個條件運算式提煉為一個獨立的函數
樣本:
double disabilityAmount(){ if(_seniority < 2){ return 0; } if(_months > 12){ return 0; } if(_isPartTime){ return 0; } }
分析:有時候你會發現一連串的檢查條件,雖然條件各不相同,但是最終行為卻一致,這種情況一般是用“邏輯與”或者“邏輯或”把他們合并為一個條件運算式,使代碼更清楚。 合并後
double disabilityAmount(){ if(isNotAbility()){ return 0 } } boolean isNotAbility(){ return ((_seniority < 2) || (_months > 12) || _isPartTime) }
4. Consolidate Duplicate Conditional Fragements(合并重複的
條件片段)
---在條件運算式的每個分支上有著相同的一段代碼
if(isBook){ total = price * 0.95; send(); }else{ total = price * 0.5; send(); }
分析: 針對這種情況,應該將重複的代碼搬移到條件運算式的外面,這樣代碼才能夠更加清
楚的表明那些東西隨條件的變化而變化,那些東西保持不變
if(isBook){ total = price * 0.95; }else{ total = price * 0.5; } send();
5. Replace Conditional with Polymorphism(以多態取代條件
運算式)
如果需要根據對象的不同類型而採取不同的行為,多態使你不需要編寫明顯的條件運算式。比如重構第一章我們提到的電影分兒童、新片、普通片。這三種片都是電影的不同類型,我們可以定義一個電影的抽象類別,然後利用多態的性質,讓不同的類型的電影去繼承,這樣我們就可以去掉多餘的條件判斷