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

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

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
  • 相关阅读:
    Linux 命令
    Linux 命令
    Linux 命令
    Linux 命令
    121.Best Time to Buy and Sell Stock---dp
    136.Single Number---异或、位运算
    141.Linked List Cycle---双指针
    Restful接口设计
    socket网络编程
    107.Binary Tree Level Order Traversal II
  • 原文地址:https://www.cnblogs.com/0616--ataozhijia/p/11611847.html
Copyright © 2011-2022 走看看