[代碼質量] 圈複雜度和代碼質量優化(附帶示例代碼糾正代碼質量)

轉載自: 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

 

代碼質量優化

———————————————————————————————————————

把子程序的一部分提取成另外一個子程序,不會下降整個程序的複雜度,只是把決策點移到其餘地方,可是這樣作能夠下降你在同一時間必須關注的複雜度水平。因爲重點是要下降你須要在頭腦中同時考慮的項目的數量,因此下降一個給定程序的複雜度是有價值的。

1.提煉函數(php爲例,下面同樣):

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
}

  

2.替換算法(把複雜算法替換爲另外一個更清晰的算法):

if($str == 'China'){
  $result = '中國人';
} else if($str == 'US'){
  $result = '美國人';
} else if($str == 'France'){
  $result = '法國人';
}

  變成這樣:

$people = [
  'China' => '中國人',
  'US' => '美國人',
  'France' => '法國人'
];
$result = $people[$str];

  

3.逆向表達(調換條件表達順序達到簡化複雜度):

if((條件1 && 條件2) || !條件1){
  return true;
} else{
  return false;
}

變成這樣:

if(條件1 && !條件2){
  return false;
}
return true;

  

4.分解條件(對複雜條件表達式(if、else)進行分解並提取成獨立函數):

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);
}

5.合併條件(將這些判斷合併爲一個條件式,並提取成獨立函數):

if($x < 1) return 0;
if($y > 10) return 0;
if($z != 0) return 0;

變成這樣:

if(get_result($x,$y,$z)) return 0;

6.移除控制標記(可使用break和return取代控制標記。):

$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;
}

7.以多態取代條件式(將整個條件式的每一個分支放進一個子類的重載方法中,而後將原始函數聲明爲抽象方法。因爲php是弱類型語言,這裏體現的有點模糊):

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";
    }
}

8.參數化方法(創建單一函數,以參數表達那些不一樣的值):

$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;
}

9.明確函數取代參數(針對該參數的每個可能值,創建一個獨立函數):

if($name == 'width'){
    $width = $value;
}
else if ($name == 'height'){
    $height = $value;
}

變成這樣:

function setWidth($value){
    $width = $value;
}
function setHeight($value){
    $height = $value;
}

參考視頻:谷歌:簡潔代碼之道

相關文章
相關標籤/搜索