圈複雜度主要與分支語句(if、else、,switch 等)的個數成正相關。可以在圖1中看到常用到的幾種語句的控制流圖(表示程式執行流程的有向圖)。當一段程式碼中含有較多的分支語句,其邏輯複雜程度就會增加。在計算圈複雜度時,可以透過程式控制流圖方便的計算出來。通常使用的計算公式是V(G) = e – n + 2 , e 代表在控制流圖中的邊的數量(對應程式碼中順序結構的部分),n 代表在控制流圖中的節點數量,包括起點和終點(1、所有終點只計算一次,即便有多個return或者throw;2、節點對應程式碼中的分支語句)。
圖1、各判斷語句的控制流圖
知道了如何計算圈複雜度,我們來使用控制流圖重新計算一次case1方法的圈複雜度,其控制流圖如下圖。狀態1表示if(num == 1 )的條件判斷,狀態2表示string=”String”的賦值操作。可以透過下面的控制流圖得到 e = 3 ; n = 3;那麼全複雜度V(G) = 3 - 3 + 2 = 2,既case1的圈複雜度為2。
圈複雜度(Cyclomatic Complexity)是一種程式碼複雜度的衡量標準。它可以用來衡量一個模組判定結構的複雜程度,數量上表現為獨立現行路徑條數,也可理解為覆蓋所有的可能情況最少使用的測試用例數。圈複雜度大說明程式程式碼的判斷邏輯複雜,可能質量低且難於測試和維護。程式的可能錯誤和高的圈複雜度有著很大關係。
下面這個例項中,單元測試的覆蓋率可以達到100%,但是很容易發現這其中已經漏掉了一個NPE的測試用例。case1方法的圈複雜度為2,因此至少需要2個用例才能完全覆蓋到其所有的可能情況。
//程式原始碼,圈複雜度為 2
public String case1(int num) {
String string = null;
if (num == 1) {
string = "String";
}
return string.substring(0);
}
//上面程式碼的單元測試程式碼
public void testCase1(){
String test1 = case1(1);
}
圈複雜度主要與分支語句(if、else、,switch 等)的個數成正相關。可以在圖1中看到常用到的幾種語句的控制流圖(表示程式執行流程的有向圖)。當一段程式碼中含有較多的分支語句,其邏輯複雜程度就會增加。在計算圈複雜度時,可以透過程式控制流圖方便的計算出來。通常使用的計算公式是V(G) = e – n + 2 , e 代表在控制流圖中的邊的數量(對應程式碼中順序結構的部分),n 代表在控制流圖中的節點數量,包括起點和終點(1、所有終點只計算一次,即便有多個return或者throw;2、節點對應程式碼中的分支語句)。
圖1、各判斷語句的控制流圖
知道了如何計算圈複雜度,我們來使用控制流圖重新計算一次case1方法的圈複雜度,其控制流圖如下圖。狀態1表示if(num == 1 )的條件判斷,狀態2表示string=”String”的賦值操作。可以透過下面的控制流圖得到 e = 3 ; n = 3;那麼全複雜度V(G) = 3 - 3 + 2 = 2,既case1的圈複雜度為2。
圖2、case1的控制流圖
在看一個計算全複雜度的例子。程式程式碼如下:
public String case2(int index, String string) {
String returnString = null;
if (index < 0) {
throw new IndexOutOfBoundsException("exception <0 ");
}
if (index == 1) {
if (string.length() < 2) {
return string;
}
returnString = "returnString1";
} else if (index == 2) {
if (string.length() < 5) {
return string;
}
returnString = "returnString2";
} else {
throw new IndexOutOfBoundsException("exception >2 ");
}
return returnString;
}
程式控制流圖:
圖3、case2的控制流圖
根據公式 V(G) = e – n + 2 = 12 – 8 + 2 = 6 。case2的圈複雜段為6。說明一下為什麼n = 8,雖然圖上的真正節點有12個,但是其中有5個節點為throw、return,這樣的節點為end節點,只能記做一個。
在開發中常用的檢測圈複雜度的工具,PMD,checkstyle都可以檢測到高複雜度的程式碼塊。在程式碼的開發中,配合各種圈複雜度的檢測外掛,將高複雜度的程式碼進行適當的拆分、最佳化,可以大大提高程式碼整體的質量,減少潛在bug存在。