轉載自: https://my.oschina.net/hjchhx/blog/1840978php
———————————————————————————————————————python
圈複雜度(Cyclomatic Complexity)是衡量計算機程序複雜程度的一種措施。它根據程序從開始到結束的線性獨立路徑的數量計算得來的。算法
圈複雜度越高,代碼就越難複雜難維護。坑就越大。。。函數
從1開始,一直往下經過程序。工具
一但遇到如下關鍵字,或者其它同類的詞,就加1:if,while,repeat,for,and,or。優化
給case語句中的每一種狀況都加1。spa
例以下面這個函數,圈複雜度爲1,意味着代碼只有一條路徑。:.net
def add(a, b): return a + b
對於有一條分支的代碼,它的圈複雜度爲 2 ,好比下面遞歸計算階乘的代碼:3d
def factorial(n): if n == 0: return 1 else: return n * factorial(n-1)
它的計算方法很簡單:code
計算公式1:V(G)=E-N+2P。其中,E表示控制流圖中邊的數量,N表示控制流圖中節點的數量,P圖的鏈接組件數目(圖的組件數是相連節點的最大集合)。由於控制流圖都是連通的,因此P爲1.
圈複雜度 | 代碼情況 | 可測性 | 維護成本 |
---|---|---|---|
1-10 | 清晰、結構化 | 高 | 低 |
11-20 | 複雜 | 中 | 中 |
21-30 | 很是複雜 | 低 | 高 |
>30 | 不可讀 | 不可測 | 很是高 |
在 Python 中可使用 mccabe 包測量程序的圈複雜度。
只須要很簡單的一行命令便可安裝mccabe
pip install mccabe
運行下面這行命令,就能夠檢測test.py的圈複雜度
python -m mccabe --min 5 test.py
其中 --min 5 是指最小容許的圈複雜度,高於5的圈複雜度則輸出出來,以下圖:
第一個輸出的結果是,91行的roundRobin函數,複雜度爲7.
除了mccabe,如今市場上也有不少檢測圈複雜度工具
工具 | 類型 | 系統平臺 | 掃描語言 |
---|---|---|---|
PMD/Checkstyle | 免費 | Windows/Linux/Mac | Java,JS |
OClint | 免費 | Mac | OC |
Coverity | 商業 | Windows/Linux/Mac | C/C++,Java,C#,OC/C++,JS,Python,Ruby,PHP |
SourceMonitor | 免費 | Windows | C/C++,C#,VB.NET,Java,Delphi,VB6,HTML |
CCM | 免費 | Windows | JS,C/C+,C# |
HFCCA | 免費 | Windows/Linux/Mac | C/C++,OC |
Lizard | 免費 | Windows/Linux/Mac | C/C++,Java,C#,JS,OC/C++,Swift,Python,Ruby,TTCN-3,PHP,Scala,GDScript |
———————————————————————————————————————
把子程序的一部分提取成另外一個子程序,不會下降整個程序的複雜度,只是把決策點移到其餘地方,可是這樣作能夠下降你在同一時間必須關注的複雜度水平。因爲重點是要下降你須要在頭腦中同時考慮的項目的數量,因此下降一個給定程序的複雜度是有價值的。
function test($number){ if($number < self::MIN_NUMBER) { $number = self::MIN_NUMBER; } for($i = 0; $i < $number; $i++){ //some code } }
能夠替換成下面這種模式:
function test($number){ $number = getMin($number); for($i = 0; $i < $number; $i++){ //some code } } function getMin($number){ if($number < self::MIN_NUMBER){ return self::MIN_NUMBER; } return $number }
if($str == 'China'){ $result = '中國人'; } else if($str == 'US'){ $result = '美國人'; } else if($str == 'France'){ $result = '法國人'; }
變成這樣:
$people = [ 'China' => '中國人', 'US' => '美國人', 'France' => '法國人' ]; $result = $people[$str];
if((條件1 && 條件2) || !條件1){ return true; } else{ return false; }
變成這樣:
if(條件1 && !條件2){ return false; } return true;
if(do_some_1($number) || do_some_2($number)){ $number = $number.$someStr1.$someStr2.'123456789'; } else{ $number = $number.$someStr3.$someStr4.'123456789'; }
變成這樣:
if(do_some_fun($number)){ $number = do_some_fun1($number); } else{ $number = do_some_fun2($number); }
if($x < 1) return 0; if($y > 10) return 0; if($z != 0) return 0;
變成這樣:
if(get_result($x,$y,$z)) return 0;
$bool = false; foreach($arrs as $arr){ if(!$bool){ if($arr == 1){ someFunction(); $bool = true; } if($arr == 2){ someFunction(); $bool = true; } } }
變成這樣:
foreach($arrs as $arr){ if($arr == 1 || $arr == 2){ someFunction(); } break; }
switch ($cat){ case ‘fish’: eatFish(); case ‘moss’: eatMoss(); } function eatFish() { echo "Whale eats fish"; } function eatMoss() { echo "Whale eat moss"; }
變成這樣:
interface Eat { function eatFish(); function eatMoss(); } class Whale implements Eat { public function eatFish() { echo "Whale eats fish"; } public function eatMoss() { echo "Whale eat moss"; } }
$result = min(lastUsage(), 100) * 0.03; if(lastUsage() > 100){ $result += (min(lastUsage(), 200) - 100) * 0.05; }
變成這樣:
$result = getMin(0,100) * 0.03; $result += getMin(100,200) * 0.03; function getMin($start, $end){ if(lastUsage() > $start){ return (min(lastUsage(),$end) - $start); } return 0; }
if($name == 'width'){ $width = $value; } else if ($name == 'height'){ $height = $value; }
變成這樣:
function setWidth($value){ $width = $value; } function setHeight($value){ $height = $value; }
參考視頻:谷歌:簡潔代碼之道