浅析代码圈复杂度及认知复杂度
Python微信訂餐小程序課程視頻
https://edu.csdn.net/course/detail/36074
Python實(shí)戰(zhàn)量化交易理財系統(tǒng)
https://edu.csdn.net/course/detail/35475
寫在開始
圈復(fù)雜度用來描述一段代碼“可測性”很好(可測性這里指需要構(gòu)建完善的覆蓋全面的單元測試需要付出多少代價),但它的設(shè)計模型很難得出一個很好的“可讀性&可維護(hù)性”的測量結(jié)果
新版soanrqube引入了認(rèn)知復(fù)雜度的概念,這個復(fù)雜度指標(biāo)彌補(bǔ)了圈復(fù)雜度的一些不足,能更準(zhǔn)確的反映一段代碼的理解成本,以及維護(hù)這段代碼的困難程度。
下面就簡要的描述下,為何認(rèn)知復(fù)雜度更適合用來評價一段代碼的可讀性及可維護(hù)性。
什么是圈復(fù)雜度?
圈復(fù)雜度(Cyclomatic complexity)是一種代碼復(fù)雜度的衡量標(biāo)準(zhǔn),在1976年由Thomas J. McCabe, Sr. 提出,目標(biāo)是為了指導(dǎo)程序員寫出更具可測性和可維護(hù)性的代碼。
它可以用來衡量一個模塊判定結(jié)構(gòu)的復(fù)雜程度,數(shù)量上表現(xiàn)為獨(dú)立路徑條數(shù),也可以理解為覆蓋所有可能的情況最少需要的測試用例數(shù)量。
代碼圈復(fù)雜度的計算方法
通常采用的計算方法為點(diǎn)邊計算法(當(dāng)然還有節(jié)點(diǎn)判定法),計算公式為:
V(G) = e – n + 2
e 代表在控制流圖中的邊的數(shù)量(對應(yīng)代碼中順序結(jié)構(gòu)的部分),n 代表在控制流圖中的節(jié)點(diǎn)數(shù)量,包括起點(diǎn)和終點(diǎn)(注:所有終點(diǎn)只計算一次,即便有多個return或者throw;節(jié)點(diǎn)對應(yīng)代碼中的分支語句)
假定有如下這樣一段代碼:
根據(jù)公式 V(G) = e – n + 2 = 12 – 8 + 2 = 6 ,上圖的圈復(fù)雜段為6。
注:說明一下為什么n = 8,雖然圖上的真正節(jié)點(diǎn)有12個,但是其中有5個節(jié)點(diǎn)為throw、return,這樣的節(jié)點(diǎn)為end節(jié)點(diǎn),只能記做一個
為什么要引入認(rèn)知復(fù)雜度?
圈復(fù)雜度最初的目的是用來識別“難以測試和維護(hù)的軟件模塊”,它能算出最少的全覆蓋的測試用例量,但是不能測出一個讓人滿意的“理解難度”。
這是因?yàn)橥瑯尤?fù)雜度的代碼,不一定會具有相同的可維護(hù)性,我們看看下面的兩個例子:
上面這兩段代碼具有相同的圈復(fù)雜度,但顯然不具有相同的可讀性和可維護(hù)性性,這就是圈復(fù)雜度的不足之處。
因?yàn)槿?fù)雜度理論是在1976年提出的,它不包含一些現(xiàn)代的語言結(jié)構(gòu),比如try-catch、lambda。
并且,每個方法都默認(rèn)有一個最小圈復(fù)雜度1,這就讓我們無從得知,一個給定的類如果圈復(fù)雜度很高,它是一個大的易維護(hù)的類,還是一個很小很復(fù)雜的類。
為了解決上述這些問題,所以引入了“認(rèn)知復(fù)雜度”,它將一段代碼被閱讀和理解時的復(fù)雜程度,估算成一個具體數(shù)字
認(rèn)知復(fù)雜度如何評判?
認(rèn)知復(fù)雜度評定基本原則
- 對線性的代碼邏輯中,出現(xiàn)一個打斷邏輯的東西,復(fù)雜度+1;
- 當(dāng)打斷邏輯的是一個嵌套時,復(fù)雜度+1;
- 忽略簡寫:把多句代碼縮寫為一句可讀的代碼,復(fù)雜度不會額外增加;
上面這種描述可能有點(diǎn)抽象,具體一點(diǎn)說,以下控制流結(jié)構(gòu)會導(dǎo)致認(rèn)知復(fù)雜度增加:
for, while, do while, 三元運(yùn)算符, if/elif/else, catch語句, 跳轉(zhuǎn)語句(goto/break/continue), 以及嵌套的控制流(每一層嵌套復(fù)雜度遞增)
我們繼續(xù)拿上面提到的兩個例子舉例:
圈復(fù)雜度對于getWord方法本身會默認(rèn)有1的復(fù)雜度,每多一個case復(fù)雜度+1,所以最終圈復(fù)雜度為4
而認(rèn)知復(fù)雜度,對于整個 switch 結(jié)構(gòu)只增加1的復(fù)雜度,因?yàn)閺目衫斫狻⒖删S護(hù)程度來說,多幾個case并不會導(dǎo)致其增加(當(dāng)然,大量的case也是我們應(yīng)當(dāng)盡力去避免的)
我們接著看另外一個例子:
如你所看到的,認(rèn)知復(fù)雜度考慮到了使這個方法比前面提到的getWords()方法更難理解的因素——嵌套以及跳轉(zhuǎn)語句
因此,雖然這兩個方法的圈復(fù)雜度是一樣的,但是它們的認(rèn)知復(fù)雜度數(shù)據(jù)很好的反映了它們兩者在可理解性/可維護(hù)性上的差異。
另外,相對于圈復(fù)雜度默認(rèn)所有方法至少有1的復(fù)雜度,認(rèn)知復(fù)雜度并沒有這樣一個評定規(guī)則,這對于entity等簡單類的復(fù)雜度評判會更加友好和客觀:
綜上所述,認(rèn)知復(fù)雜度作為代碼的“可讀性/可維護(hù)性”評定指標(biāo)會更加合適。
附、代碼復(fù)雜度與軟件質(zhì)量關(guān)系
以上復(fù)雜度數(shù)值可以理解為方法粒度,即如果某一個方法復(fù)雜度>30,那這個方法的可讀性和可維護(hù)性就很低了
總結(jié)
以上是生活随笔為你收集整理的浅析代码圈复杂度及认知复杂度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这个CDR插件让你让你工作飞起来,拒绝加
- 下一篇: CC1310如何使用内部LDO