上期我們分享了關于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;
}
用法:
- 針對參數的每一種可能值,新建一個明確的函數
- 修改條件表達式的每個分支骡湖,使其調用合適的新函數
- 適用場景:函數完全取決于參數值而采取不同行為