zoukankan      html  css  js  c++  java
  • [代码质量] 圈复杂度和代码质量优化(附带示例代码纠正代码质量)

    转载自: https://my.oschina.net/hjchhx/blog/1840978

    什么是圈复杂度?

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

    圈复杂度(Cyclomatic Complexity)是衡量计算机程序复杂程度的一种措施。它根据程序从开始到结束的线性独立路径的数量计算得来的。

    圈复杂度越高,代码就越难复杂难维护。坑就越大。。。

    • 从1开始,一直往下通过程序。

    • 一但遇到以下关键字,或者其它同类的词,就加1:if,while,repeat,for,and,or。

    • 给case语句中的每一种情况都加1。

    例如下面这个函数,圈复杂度为1,意味着代码只有一条路径。:

    def add(a, b):
        return a + b
    

      

    对于有一条分支的代码,它的圈复杂度为 2 ,比如下面递归计算阶乘的代码:

    def factorial(n):
      if n == 0:
        return 1
      else:
        return n * factorial(n-1)

    它的计算方法很简单:

    计算公式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;
    }

    参考视频:谷歌:简洁代码之道

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
  • 相关阅读:
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & ManacherK
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher J
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher I
    pat 1065 A+B and C (64bit)(20 分)(大数, Java)
    pat 1069 The Black Hole of Numbers(20 分)
    pat 1077 Kuchiguse(20 分) (字典树)
    pat 1084 Broken Keyboard(20 分)
    pat 1092 To Buy or Not to Buy(20 分)
    pat 1046 Shortest Distance(20 分) (线段树)
    pat 1042 Shuffling Machine(20 分)
  • 原文地址:https://www.cnblogs.com/0616--ataozhijia/p/11611847.html
Copyright © 2011-2022 走看看