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

上期咱們分享了關於Java中equals與hashCode的理解java

本期咱們將分享Java中if/else複雜邏輯的處理git

在github上曾看到一些issue,國外的程序員比較忌諱寫else,看到了不少這樣的評論else is horrible,那麼對於邏輯很複雜的代碼段,若是用太多的if/else的話,那麼會致使代碼的閱讀難度變大,同時會增長代碼的圈複雜度,理論上,若是一個函數的圈複雜度超過8,那麼這個函數就還有可優化的地方,那麼如何優化這種多分支的複雜邏輯的函數呢?手冊中給出了三種方法:衛語句策略模式狀態模式,經過閱讀《重構:改善既有代碼的設計》發現,解決這個問題其實有不少種,下面咱們就一一道來。程序員

第一大類:從新組織函數

一、Extract Method(提煉函數)

這種方法應該是最經常使用的方法之一,當函數過長或者分支太多的話,就能夠考慮將其中的一段代碼提煉成一個獨立的函數。github

  • 原始代碼:
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.");
}
複製代碼

用法:算法

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

二、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 "";
}
複製代碼

用法:併發

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

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

一、Eecompose Conditional(分解條件表達式)

若是有複雜的條件(if-then-else)語句,從if、then、else三個段落中分別提煉出獨立函數。ide

  • 原始代碼:
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段落都提煉出來,各自構成一個獨立函數
  • 適用場景:複雜的條件語句。若是發現嵌套的條件邏輯,先觀察是否可使用衛語句,若是不行,再開始分解其中的每一個條件

二、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();
}
複製代碼

用法:

  • 肯定這些條件語句都沒有反作用
  • 使用適當的邏輯操做符,將一系列相關條件表達式合併爲一個,並對合並後的表達式提煉函數
  • 適用場景:一系列條件測試,都獲得相同的結果

三、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();
}
複製代碼

用法:

  • 鑑別出執行方式不隨條件變化而變化的的代碼
  • 若是這些共同代碼位於條件表達式起始處,就將它移到條件表達式以前,若是在尾端,移到條件表達式以後
  • 適用場景:在條件表達式的每一個分支上有相同的一段代碼

四、Remove Control Flag(移除控制標記)

在一系列布爾表達式中,某個變量帶有「控制標記(Flag)」的做用,以break語句或者return語句取代控制標記。

五、Replace Nested Confitional with Guard Clauses(以衛語句取代嵌套條件表達式)

若是多個分支都屬於正常行爲,就應該使用if...else...的條件表達式,若是某個條件極其罕見,就應該單獨檢查該條件,並在該條件爲真時馬上從函數中返回,這樣的單獨檢查經常被稱爲衛語句。

  • 原始代碼:
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;
}
複製代碼

用法:

  • 對於每一個檢查,放進一個衛語句,衛語句要麼就從函數中返回,要麼就拋出一個異常
  • 每次將條件檢查替換成衛語句後,編譯並測試:若是全部的衛語句都致使一樣的結果,請使用合併條件表達式
  • 適用場景:使用衛語句返回全部特殊狀況

六、Replace Conditional with Polymorphism(以多態取代條件表達式)

條件表達式,根據對象的類型選擇不一樣的行爲,將這個條件表達式的每一個分支放進一個子內的覆寫函數中,而後將原始函數聲明爲抽象函數,這一項就是手冊中說的策略模式以及狀態模式。 正由於有了多態,因此「類型碼的switch語句」以及「基於類型名稱的if-then-else」語句在面向對象程序中不多出現。

第三大類:簡化函數調用

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

某個函數既返回對象狀態值,又修改了狀態,創建兩個不一樣的函數,其中一個負責查詢,另外一個負責修改。

  • 併發的狀況:須要保留第三個函數來同時作這兩件事

二、Parameterize Method(令函數攜帶參數)

若干函數作了相似的工做,但在函數本體中卻包含了不一樣的值,創建單一函數,以參數表達那些不一樣的值。

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

public void fivePercentRaise() {
	salary *= 1.05;
}
複製代碼
  • 改後代碼:
public void raise(double factor) {
	salary *= (1 + factor);
}
複製代碼
  • 要點在於:以可將少許數值視爲參數爲依據,找出帶有重複性的代碼

三、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;
}
複製代碼

用法:

  • 針對參數的每一種可能值,新建一個明確的函數
  • 修改條件表達式的每一個分支,使其調用合適的新函數
  • 適用場景:函數徹底取決於參數值而採起不一樣行爲

微信公衆號: 碼上論劍
請關注個人我的技術微信公衆號,訂閱更多內容
相關文章
相關標籤/搜索