《重構(gòu)》學(xué)習(xí)筆記(07)-- 簡(jiǎn)化條件表達(dá)式

條件邏輯有可能十分復(fù)雜,復(fù)雜的條件邏輯可能讓復(fù)雜度快速上升,并有可能導(dǎo)致代碼難以理解。因此,需要一些手段,來簡(jiǎn)化它們。

Decompose Conditional(分解條件表達(dá)式)

帶有復(fù)雜邏輯的函數(shù)中,代碼會(huì)告訴你發(fā)生的事情。但是十分冗長(zhǎng)的條件表達(dá),往往讓后來者摸不著頭腦。這種情況下,可以將它分解為獨(dú)立的函數(shù),并且為新函數(shù)命名(命名可以起到自注釋的作用)。在原函數(shù)中調(diào)用新建函數(shù),從而更清楚地表達(dá)自己的意圖。舉個(gè)例子

if(date.before(SUMMER_START) || date.after(SUMMER_END))
    charge=quantity*_winterRate+_winterServiceCharge;
else
    charge=quantity*_summerRate;

可以重構(gòu)為

if(notSummer(date))
    charge=winterCharge(quantity);
else
    charge=summerCharge(quantity);

上面的例子就是將date.before(SUMMER_START) || date.after(SUMMER_END) 替代為notSummer(date),這種情況就一目了然。概括下這種重構(gòu)方法的步驟:

  • 將if段落提煉出來,構(gòu)成一個(gè)獨(dú)立的函數(shù)。
  • 將then段落和else段落都提煉出來,各自構(gòu)成一個(gè)獨(dú)立的函數(shù)。
  • 如果發(fā)現(xiàn)嵌套的條件邏輯,應(yīng)該先看看是否可以使用以衛(wèi)語句取代嵌套條件式。如果不行才開始分解其中的每個(gè)條件。

Consolidate Conditional Expression(合并條件表達(dá)式)

如果有一串條件檢查,如果檢查條件各不相同,但是最終導(dǎo)致的結(jié)果卻一致。那么,就使用“邏輯或”和“邏輯與”將它們合并為一個(gè)條件表達(dá)式。舉例:

 double disabilityAmount() {
        if (_seniority < 2) {
            return 0;
        }
        if (_monthsDisabled > 12) {
            return 0;
        }
        if (_isPartTime) {
            return 0;
        }
        // compute the disability amount
        // ...
    }

由于都是返回0,因此可以將條件表達(dá)式合并

    double disabilityAmount() {
        if (isNotEligibleForDisability()) {
            return 0;
        }
        // compute the disability amount
        // ...
    }

    boolean isNotEligibleForDisability() {
        return ((_seniority < 2) || (_monthsDisabled > 12) || (_isPartTime));
    }

需要注意的是,要分清返回0是系統(tǒng)的行為是否在邏輯上一致,還是只是同時(shí)返回相同數(shù)值,在未來?xiàng)l件變動(dòng)時(shí)還會(huì)有所改動(dòng)??偨Y(jié)這種重構(gòu)的做法為:

  • 確定這些條件語句都沒有副作用。
  • 使用適當(dāng)?shù)倪壿嫴僮鞣瑢⒁幌盗邢嚓P(guān)條件表達(dá)式合并為一個(gè)。
  • 編譯,測(cè)試。
  • 對(duì)合并后的條件表達(dá)式實(shí)施Extract Method。

Consolidate Duplicate Conditional Fragments(合并重復(fù)的條件片段)

你有時(shí)候會(huì)發(fā)現(xiàn),條件語句不同分支執(zhí)行了相同的片段。如果是這樣,你就需要將這段代碼搬移到條件表達(dá)式外面。舉例子:

    if (isSpecialDeal()) {
        total = price * 0.95;
       send();
    } else {
        total = price * 0.98;
        send();
   }

重構(gòu)后,可以將send()提取出來

    if (isSpecialDeal()) {
        total = price * 0.95;
    } else {
        total = price * 0.98;
    }
    send();

同樣的,如果try catch中都有同樣的代碼,就可以放在final中去執(zhí)行。總結(jié)這種重構(gòu)的做法為:

  • 鑒別出”執(zhí)行方式不隨條件變化而變化”的代碼。
  • 如果這些共通代碼位于條件表達(dá)式起始處,就將它移到條件表達(dá)式之前。
  • 如果這些共通代碼位于條件表達(dá)式尾端,就將它移到條件表達(dá)式之后。
  • 如果這些共通代碼位于條件表達(dá)式中段,就需要觀察共通代碼之前或之后的代碼是否改變了什么東西,如果的確有所改變,應(yīng)該首先將共通代碼向前或向后移動(dòng),移至條件表達(dá)式的起始處或尾端,再以前面所受的辦法來處理。
  • 如果共通代碼不止一條語句,應(yīng)該先使用Extract Method(提煉函數(shù))將共通代碼提煉到一個(gè)獨(dú)立函數(shù)中,再以前面所說的辦法來處理。

Remove Control Flag(移除控制標(biāo)記)

在一系列的布爾表達(dá)式中,某個(gè)變量帶有“控制標(biāo)記”的作用,那么用break或者return語句取代控制標(biāo)記。這樣的控制標(biāo)記帶來的麻煩通常超過帶來的便利。結(jié)構(gòu)化編程告訴我們一個(gè)原則:每個(gè)子程序都只能有一個(gè)入口和一個(gè)出口,但是“單一出口”原則通常讓我們?cè)诖a中加入許多的控制標(biāo)記,這樣就大大降低了表達(dá)式的可讀性。使用break或者return可以讓程序條理更加清晰。舉例:

function checkSecurity(peoples) {
  String found = '';
  for(let i = 0; i < peoples.length; i++) {
    if(!found) {
      if(peoples[i] === 'Don' || peoples[i] === 'John') {
        sendAlert();
        found = peoples[i];
      }
    }
  }
  someLaterCode(found);
}

重構(gòu)為

function checkSecurity(peoples) {
  String found = foundMiscreant(peoples);
  someLaterCode(found);
}

function foundMiscreant(peoples) {
  for(let i = 0; i < peoples.length; i++) {
    if(peoples[i] === 'Don' || peoples[i] === 'John') {
      sendAlert();
      return peoples[i];
    }
  }
  return '';
}

這種重構(gòu)的一般做法為:
使用extract method,整段邏輯提煉到一個(gè)獨(dú)立函數(shù)中

  • 找出跳出這段邏輯的控制標(biāo)記值
  • 找出對(duì)標(biāo)記變量賦值的語句,代以恰當(dāng)?shù)膔eturn語句.
  • 替換完成后,編譯并測(cè)試。

Replace Nested Conditional with Guard Clauses(以衛(wèi)語句取代嵌套條件表達(dá)式)

如果if else分支都屬于正常行為,那么建議正常使用if else去表達(dá)。但是如果else分支中的情況極其罕見,那么建議采用衛(wèi)語句進(jìn)行表現(xiàn)。

function getPayAmount () {
  let result
  if (isDead) result = deadAmount()
  else {
    if (isSeparated) result = separatedAmount()
    else {
      if (isRetired) result = retiredAmount()
      else result = normalPayAmount()
    }
  }
  return result
}

可以重構(gòu)為:

function getPayAmount () {
  if (isDead) return deadAmount()
  if (isSeparated) return separatedAmount()
  if (isRetired) return retiredAmount()
  return normalPayAmount()
}

這樣代碼條理更加清晰,可讀性更高。這種重構(gòu)方法的做法為:

  • 對(duì)于每個(gè)檢查,放進(jìn)一個(gè)衛(wèi)語句(衛(wèi)語句要不就從函數(shù)返回,要不就拋出一個(gè)異常)。
  • 編譯測(cè)試。

Replace Conditional with Polymorphism(以多態(tài)取代條件表達(dá)式)

“多態(tài)”是面向?qū)ο蟮木杷冢鄳B(tài)可以避免為不同類型代入不同行為。舉個(gè)例子:

var makeSound = function(animal) {
    if(animal instanceof Duck) {
        console.log('嘎嘎嘎');
    } else if (animal instanceof Chicken) {
        console.log('咯咯咯');
    }
}
var Duck = function(){}
var Chiken = function() {};
makeSound(new Chicken());
makeSound(new Duck());

重構(gòu)后

var makeSound = function(animal) {
    animal.sound();
}

var Duck = function(){}
Duck.prototype.sound = function() {
    console.log('嘎嘎嘎')
}
var Chiken = function() {};
Chiken.prototype.sound = function() {
    console.log('咯咯咯')
}

makeSound(new Chicken());
makeSound(new Duck());

多態(tài)背后的思想是將”做什么“和”誰去做以及怎樣去做分開“。通常的做法是:

  • 如果要處理的條件表達(dá)式是一個(gè)更大函數(shù)中的一部分。首先,對(duì)條件表達(dá)式進(jìn)行分析,然后使用extract method將其提煉到一個(gè)獨(dú)立函數(shù)去。
  • 如果由必要,使用move method、將條件表達(dá)式放置到繼承結(jié)構(gòu)的頂端。
  • 任選一個(gè)子類,在其中建立一個(gè)函數(shù),使之覆寫超類中容納條件表達(dá)式的那個(gè)函數(shù)。將與該子類相關(guān)的條件表達(dá)式分支復(fù)制到新建函數(shù)中,并對(duì)其進(jìn)行適當(dāng)調(diào)整。
    可能需要將超類中某些private字段聲明為protected。
  • 超類中刪掉條件表達(dá)式內(nèi)被復(fù)制了的分支。
  • 將超類中容納條件表達(dá)式的函數(shù)聲明為抽象函數(shù)。

Introduce Null Object(引入Null對(duì)象)

這種重構(gòu)方法在前端中不知如何使用,暫忽略。

Introduce Assertion(引入斷言)

這種情況在程序中一般使用是非空判斷,為了減少圈復(fù)雜度。我建議進(jìn)行直接的空判斷,而不是非空判斷。
舉例

if(response){
   //do something
}

可以重構(gòu)為

if(!response){
    return;
}
//do something

本章重構(gòu)方法比較簡(jiǎn)單,總的原則就是讓代碼清晰可懂,減少復(fù)雜度。這樣的代碼維護(hù)和交接都是一件非常愉悅的事情。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容