阿里Java開發(fā)手冊思考(二)

題圖:by pixel2013 From pixabay

上期我們分享了關于Java中equals與hashCode的理解

本期我們將分享Java中if/else復雜邏輯的處理

在github上曾看到一些issue壮锻,國外的程序員比較忌諱寫else雀费,看到了很多這樣的評論else is horrible半夷,那么對于邏輯很復雜的代碼段,如果用太多的if/else的話念祭,那么會導致代碼的閱讀難度變大,同時會增加代碼的圈復雜度,理論上功偿,如果一個函數的圈復雜度超過8,那么這個函數就還有可優(yōu)化的地方往堡,那么如何優(yōu)化這種多分支的復雜邏輯的函數呢脖含?手冊中給出了三種方法:衛(wèi)語句策略模式投蝉、狀態(tài)模式养葵,通過閱讀《重構:改善既有代碼的設計》發(fā)現(xiàn),解決這個問題其實有很多種瘩缆,下面我們就一一道來关拒。

第一大類:重新組織函數

1、Extract Method(提煉函數)

這種方法應該是最常用的方法之一庸娱,當函數過長或者分支太多的話着绊,就可以考慮將其中的一段代碼提煉成一個獨立的函數。

  • 原始代碼:
public void today() {
    if (isBusy()) {
        System.out.println("change time.");
    } else if (isFree()) {
        System.out.println("go to travel.");
    } else {
        System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
    }
}
  • 改后代碼:
public void today() {
    if (isBusy()) {
        changeTime();
    } else if (isFree()) {
        goToTravel();
    } else {
        stayAtHomeToLearn();
    }
}

private void changeTime() {
        System.out.println("change time.");
}

private void goToTravel() {
    System.out.println("go to travel.");
}

private void stayAtHomeToLearn() {
    System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
}

用法:

  • 提煉新函數熟尉,根據這個函數的意圖來命名归露,以它做什么來命名,而不是以他怎么做來命名
  • 仔細檢查提煉出的代碼是否引用了作用域限于源函數的變量斤儿,包括局部變量和源函數參數
  • 適用場景:函數過長或者需要注釋才能讓人理解用途的代碼

2剧包、Substitute Algorithm(替換算法)

把某個算法替換成另一個更清晰的算法,或將函數本體替換為另一個算法往果。

  • 原始代碼:
public String foundPerson(String[] people) {
    for (int i = 0; i < people.length; i++) {
        if (people[i].equals("Don")) {
            return "Don";
        }
        if (people[i].equals("John")) {
            return "John";
        }
        if (people[i].equals("Kent")) {
            return "Kent";
        }
    }
    return "";
}
  • 改后代碼:
public String foundPerson(String[] people) {
    List<String> candidates = Arrays.asList(new String[] { "Don", "John", "Kent" });
    for (int i = 0; i < people.length; i++) {
        if (candidates.contains(people[i])) {
            return people[i];
        }
    }
    return "";
}

用法:

  • 準備好另一個替換用的算法
  • 新算法疆液,要與原本的算法結果相同
  • 適用場景:把某個算法替換為更清晰的算法,或者把函數替換為一個算法

第二大類:簡化條件表達式

1陕贮、Eecompose Conditional(分解條件表達式)

如果有復雜的條件(if-then-else)語句堕油,從if、then肮之、else三個段落中分別提煉出獨立函數掉缺。

  • 原始代碼:
public void today() {
    if (isBusy() || isNotWeekend()) {
       System.out.println("change time.");
    return;
    } else {
        System.out.println("go to travel.");
    }
}
  • 改后代碼:
public void today() {
    if (notFree()) {
        changeTime();
    } else {
        goToTravel();
    }
}

private boolean notFree() {
    return isBusy() || isNotWeekend();
}

private void changeTime() {
    System.out.println("change time.");
}

private void goToTravel() {
    System.out.println("go to travel.");
}

用法:

  • 將if段落提煉出來,構成一個獨立函數
  • 將then段落和else段落都提煉出來戈擒,各自構成一個獨立函數
  • 適用場景:復雜的條件語句眶明。如果發(fā)現(xiàn)嵌套的條件邏輯,先觀察是否可以使用衛(wèi)語句峦甩,如果不行赘来,再開始分解其中的每個條件

2现喳、Consolidate Conditioinal Expression(合并條件表達式)

如果有一系列條件測試,都得到相同結果犬辰,將這些測試合并為一個條件表達式嗦篱,并將這個條件表達式提煉成為一個獨立函數。

  • 原始代碼:
public void today() {
    if (isWeekend()) {
        System.out.println("go to travel.");
    }
    if (isHoliday()) {
        System.out.println("go to travel.");
    }
    if (noWork()) {
        System.out.println("go to travel.");
    }
}
  • 改后代碼:
public void today() {
    if (isFree()) {
        System.out.println("go to travel.");
    }
}

private boolean isFree() {
    return isWeekend() || isHoliday() || noWork();
}

用法:

  • 確定這些條件語句都沒有副作用
  • 使用適當的邏輯操作符幌缝,將一系列相關條件表達式合并為一個灸促,并對合并后的表達式提煉函數
  • 適用場景:一系列條件測試,都得到相同的結果

3涵卵、Consolidate Duplicate Conditional Clauses(合并重復的條件判斷)

在條件表達式的每個分支上有相同的一段代碼浴栽,將這段代碼搬移到條件表達式之外。

  • 原始代碼:
public void today() {
    if (isBusy()) {
        System.out.println("change time.");
        sleep();
    } else if (isFree()) {
        System.out.println("go to travel.");
        sleep();
    } else {
        System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
        sleep();
    }
}
  • 改后代碼:
public void today() {
    if (isBusy()) {
        System.out.println("change time.");
    } else if (isFree()) {
        System.out.println("go to travel.");
    } else {
        System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
    }
    sleep();
}

用法:

  • 鑒別出執(zhí)行方式不隨條件變化而變化的的代碼
  • 如果這些共同代碼位于條件表達式起始處轿偎,就將它移到條件表達式之前典鸡,如果在尾端,移到條件表達式之后
  • 適用場景:在條件表達式的每個分支上有相同的一段代碼

4坏晦、Remove Control Flag(移除控制標記)

在一系列布爾表達式中萝玷,某個變量帶有“控制標記(Flag)”的作用,以break語句或者return語句取代控制標記昆婿。

5球碉、Replace Nested Confitional with Guard Clauses(以衛(wèi)語句取代嵌套條件表達式)

如果多個分支都屬于正常行為,就應該使用if...else...的條件表達式仓蛆,如果某個條件極其罕見睁冬,就應該單獨檢查該條件,并在該條件為真時立刻從函數中返回看疙,這樣的單獨檢查常常被稱為衛(wèi)語句豆拨。

  • 原始代碼:
public void today() {
    if (isBusy()) {
        System.out.println("change time.");
    } else if (isFree()) {
        System.out.println("go to travel.");
    } else {
        System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
    }
}
  • 改后代碼:
public void today() {
    if (isBusy()) {
        System.out.println("change time.");
        return;
    }
    if (isFree()) {
        System.out.println("go to travel.");
        return;
    }
    System.out.println("stay at home to learn Alibaba Java Coding Guidelines.");
    return;
}

用法:

  • 對于每個檢查,放進一個衛(wèi)語句狼荞,衛(wèi)語句要么就從函數中返回辽装,要么就拋出一個異常
  • 每次將條件檢查替換成衛(wèi)語句后帮碰,編譯并測試:如果所有的衛(wèi)語句都導致同樣的結果相味,請使用合并條件表達式
  • 適用場景:使用衛(wèi)語句返回所有特殊情況

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

條件表達式殉挽,根據對象的類型選擇不同的行為丰涉,將這個條件表達式的每個分支放進一個子內的覆寫函數中,然后將原始函數聲明為抽象函數斯碌,這一項就是手冊中說的策略模式以及狀態(tài)模式一死。
正因為有了多態(tài),所以“類型碼的switch語句”以及“基于類型名稱的if-then-else”語句在面向對象程序中很少出現(xiàn)傻唾。

第三大類:簡化函數調用

1投慈、Separate Query from Modifier(將查詢函數和修改函數分離)

某個函數既返回對象狀態(tài)值承耿,又修改了狀態(tài),建立兩個不同的函數伪煤,其中一個負責查詢加袋,另一個負責修改。

  • 并發(fā)的情況:需要保留第三個函數來同時做這兩件事

2抱既、Parameterize Method(令函數攜帶參數)

若干函數做了類似的工作职烧,但在函數本體中卻包含了不同的值,建立單一函數防泵,以參數表達那些不同的值蚀之。

  • 原始代碼:
public void tenPercentRaise() {
    salary *= 1.1;
}

public void fivePercentRaise() {
    salary *= 1.05;
}
  • 改后代碼:
public void raise(double factor) {
    salary *= (1 + factor);
}
  • 要點在于:以可將少量數值視為參數為依據,找出帶有重復性的代碼

3捷泞、Replace Parameter with Explicit Methods(以明確函數取代參數)

有一個函數足删,其中完全取決于參數值而采取不同行為,針對該參數的每一個可能值锁右,建立一個獨立的函數壹堰。

  • 原始代碼:
public void setValue(String name, int value) {
    if (name.equals("height")) {
        height = value;
    }

    if (name.equals("width")) {
        width = value;
    }
}
  • 改后代碼:
public void setHeight(int arg) {
    height = arg;
}

public void setWidth(int arg) {
    width = arg;
}

用法:

  • 針對參數的每一種可能值,新建一個明確的函數
  • 修改條件表達式的每個分支骡湖,使其調用合適的新函數
  • 適用場景:函數完全取決于參數值而采取不同行為
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末贱纠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子响蕴,更是在濱河造成了極大的恐慌谆焊,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浦夷,死亡現(xiàn)場離奇詭異辖试,居然都是意外死亡,警方通過查閱死者的電腦和手機劈狐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門罐孝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肥缔,你說我怎么就攤上這事莲兢。” “怎么了续膳?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵改艇,是天一觀的道長。 經常有香客問我坟岔,道長谒兄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任社付,我火速辦了婚禮承疲,結果婚禮上邻耕,老公的妹妹穿的比我還像新娘。我一直安慰自己燕鸽,他們只是感情好赊豌,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绵咱,像睡著了一般碘饼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悲伶,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天艾恼,我揣著相機與錄音,去河邊找鬼麸锉。 笑死钠绍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的花沉。 我是一名探鬼主播柳爽,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碱屁!你這毒婦竟也來了磷脯?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤娩脾,失蹤者是張志新(化名)和其女友劉穎赵誓,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體柿赊,經...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡俩功,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了碰声。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诡蜓。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胰挑,靈堂內的尸體忽然破棺而出蔓罚,到底是詐尸還是另有隱情,我是刑警寧澤洽腺,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布脚粟,位于F島的核電站,受9級特大地震影響蘸朋,放射性物質發(fā)生泄漏。R本人自食惡果不足惜扣唱,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一藕坯、第九天 我趴在偏房一處隱蔽的房頂上張望团南。 院中可真熱鬧,春花似錦炼彪、人聲如沸吐根。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拷橘。三九已至,卻和暖如春喜爷,著一層夾襖步出監(jiān)牢的瞬間冗疮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工檩帐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留术幔,地道東北人。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓湃密,卻偏偏與公主長得像诅挑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子泛源,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內容