循環複雜度

循環複雜度Cyclomatic complexity)也稱為條件複雜度圈复杂度,是一種軟體度量,是由老托馬斯·J·麥凱布英语Thomas J. McCabe, Sr.在1976年提出,用來表示程式的複雜度,其符號為VG或是M。循環複雜度由程式的源代碼中量測線性獨立路徑的個數。此概念有些類似的量測文字複雜度的Flesch-Kincaid可讀性測試英语Flesch-Kincaid Readability Test ,不過方法不完全相同。

循環複雜度是由程式的控制流圖來計算:有向圖的節點對應程式中個別的程式碼,而若一個程式執行後會立刻執行另一程式碼,會有邊連結二程式碼對應的節點。圈複雜度可應用在程式的子程序模組方法類別

麥凱布首先提出一種稱為「基礎路徑測試」(Basis Path Testing)的軟體測試方式,是測試程式中的每一線性獨立路徑,此情形的測試用例個數即為程式的循環複雜度[1]

「循環複雜度」的名稱有時會讓人誤解,因為此複雜度不只計算程式中的迴圈(循環)個數。循環複雜度是指程式的控制流圖中,若將結束點到啟始點再增加一個邊時,控制流圖中的圈(幾個邊形成封閉路徑)的個數[2]

原理

 
簡單程式的控制流圖。此程式由紅色的節點開始執行,然後進入迴圈(紅色節點下由三個節點組成),離開迴圈後有條件分支,最後執行藍色節點後結束,此控制流圖中,E = 9, N = 8, P = 1,因此其循環複雜度為 9 - 8 + (2*1) = 3

一段程式的循環複雜度是其線性獨立路徑的數量。若程式中沒有像IF指令或FOR迴圈的控制流程,因為程式中只有一個路徑,其循環複雜度為1,若程式中有一個IF指令,會有二個不同路徑,分別對應IF條件成立及不成立的情形,因此循環複雜度為2。

數學上,一個結構化程式[note 1]的循環複雜度是利用程式的控制流圖來定義,控制流圖是一個有向圖,圖中的節點為程式的基礎模塊,若一個模塊結束後,可能會執行另一個模塊,則用箭頭連結二個模塊,並標示可能的執行順序。循環複雜度M可以用下式定義[2]

M = EN + 2P

其中

E 為圖中邊的個數
N 為圖中節點的個數
P 為圖中連通分量的個數
 
對應同一個程式的控制流圖,但多加一個從結束點到啟始點的邊,因此為強連通的控制流圖,若利用此圖計算循環複雜度,其公式為M=E-N+P,而E = 10、N = 8、P = 1,因此循環複雜度為3

另一個計算循環複雜度的公式,需修改控制流圖,每一個結束點都增加一個到啟始點的邊。修改後的圖稱為強連通,任何二個節點A和B,都可以找到從A到B及從B到A的路徑。程式的循環複雜度等於此圖中迴路的個數(也稱為第一貝蒂數),其公式如下[2]

M = EN + P

上式可以視為計算圖中線性獨立迴路(迴路內不包括其他迴路)的個數,由於控制流圖增加結束點到啟始點的邊,因此對應一個結束點至少會有一個迴路。

對於單一的程式(或副程式或方法),P恆為1。但循環複雜度可以適用於同時分析許多程式或副程式的情形(例如針對一個類別中的所有方法),此時P等於程式的個數,因為每一個程式的圖都是一個獨立的連接元件。若每一個程式都只一個結束點,P也可以視為是結束點的個數。

可以證明任何只有一個進入點及結束點的結構化程式,其循環複雜度等於程式中決策點(if指令及條件迴圈)個數加1[2][3]

循環複雜度也可以延伸到多個結束點的程式,此時的循環複雜度如下:

π - s + 2

其中

π是程式中決策點的個數
s為結束點的個數[3][4]

應用

限制軟體複雜度

麥凱布提出循環複雜度時,其原始目的之一就是希望在軟體開發過程中就限制其複雜度。他建議程式設計者需計算其開發模組的複雜度,若一模組的循環複雜度超過10,需再分割為更小的模組[2]。NIST(國家標準技術研究所)的結構化測試方法論已此作法略作調整,在一些特定情形下,模組循環複雜度上限放寬到15會比較合適。此方法論也承認有些特殊情形下,模組的複雜度需要超過上述的上限,其建議為「模組的循環複雜度需在上限範圍以內,否則需提供書面資料,說明為何此模組循環複雜度有必要超過上限。」[5]

模組內聚性的評估

可以預期一個複雜度較高模組的內聚性會比較低,至少不會到功能內聚性的程度。一個有高複雜度及低內聚性的模組中會有許多的決策點,這類的模組多半執行超過一個明確定義的任務,因此內聚性較低。一個2005年的研究發現複雜度的度量和由專家評估的模組內聚性有高度負相關,反而針對內聚性設計的度量和專家評估結果之間的相關性還比較不明顯[6]

推測軟體缺陷個數

許多研究指出一模組及方法的循環複雜度和其中的缺陷個數有相關性,許多這類研究發現循環複雜度和缺陷個數有高度的正相關:循環複雜度最高的模組及方法,其中的缺陷個數也最多。

不過,有些研究是在控制模組大小相近的情形下進行分析(例如比較二個源代碼行數相近,但循環複雜度不同模組的缺陷個數),許多這類的研究發現循環複雜度和缺陷個數沒有明顯相關,不過仍有一些研究認為在此情形下二者仍有相關性。有些此領域的研究者認為那些研究結果循環複雜度和缺陷個數沒有明顯相關的研究,其研究方法的有效性可能有問題[7]

萊斯·哈頓英语Les Hatton認為利用循環複雜度來預測缺陷個數,和利用源代碼行數來預測缺陷個數的結果大致相近[8]

相關條目

註解

  1. ^ 此處的結構化特別強調一個函式只有單一的結束點

參考資料

  1. ^ A J Sojev. Basis Path Testing. [2012-09-30]. (原始内容存档于2012-04-26). 
  2. ^ 2.0 2.1 2.2 2.3 2.4 McCabe. A Complexity Measure (PDF). IEEE Transactions on Software Engineering. December 1976: 308–320 [2012-10-01]. (原始内容 (PDF)存档于2022-05-30). 
  3. ^ 3.0 3.1 Belzer, Kent, Holzman and Williams. Encyclopedia of Computer Science and Technology. CRC Press. 1992: 367–368. 
  4. ^ Harrison. Applying Mccabe's complexity measure to multiple-exit programs. Software: Practice and Experience (J Wiley & Sons). October 1984. 
  5. ^ McCabe, Watson. Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric. 1996. (原始内容存档于2012-08-28). 
  6. ^ Stein; Cox, Glenn; Etzkorn, Letha; et al. Exploring the relationship between cohesion and complexity. Journal of Computer Science. 2005, 1 (2): 137–144 [2012-10-02]. doi:10.3844/jcssp.2005.137.144. (原始内容存档于2019-12-02). 
  7. ^ Kan. Metrics and Models in Software Quality Engineering. Addison-Wesley. 2003: 316–317. ISBN 0-201-72915-6. 
  8. ^ Les Hatton. The role of empiricism in improving the reliability of future software. 2008. version 1.1. (原始内容存档于2012-11-03).