[代码质量] 代码质量管控 — 复杂度检测 (JavaScript)

代码的复杂度是评估一个项目的重要标准之一。较低的复杂度既能减少项目的维护成本,又能避免一些不可控问题的出现。然而在日常的开发中却没有一个明确的标准去衡量代码结构的复杂程度,大家只能凭着经验去评估代码结构的复杂程度,比如,代码的程度、结构分支的多寡等等。当前代码的复杂度到底是个什么水平?什么时候就需要我们去优化代码结构、降低复杂度?这些问题我们不得而知。
因此,我们需要一个明确的标准去衡量代码的复杂度。

Litmus 是我们团队建设的一个代码质量检测系统,目前包括代码的风格检查、重复率检查以及复杂度检查。litmus 采用代码的 Maintainability(可维护性)来衡量一个代码的复杂度,并且通过以下三个方面来定义一段代码的 Maintainability 的值:

  • Halstead Volume(代码容量)
  • Cyclomatic Complexity(圈复杂度)
  • Lines of Code(代码行数)
Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171)<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span>

代码行数不做赘述,下面我们具体介绍代码容量、圈复杂的含义以及它们的计算原理

代码的容量关注的是代码的词汇数,有以下几个基本概念

参数含义 n1 Number of unique operators,不同的操作元(运算子)的数量 n2 Number of unique operands,不同的操作数(算子)的数量 N1 Number of total occurrence of operators,为所有操作元(运算子)合计出现的次数 N2 Number of total occurrence of operands,为所有操作数(算子)合计出现的次数 Vocabulary n1 + n2,词汇数 length N1 + N2,长度 Volume length * Log2 Vocabulary,容量

一个例子

<span class="hljs-keyword">function tFunc(opt) {
    <span class="hljs-built_in">let result = opt + 1;
    <span class="hljs-built_in">return result;
}
// n1&#xFF1A;<span class="hljs-keyword">function&#xFF0C;<span class="hljs-built_in">let&#xFF0C;=&#xFF0C;+&#xFF0C;<span class="hljs-built_in">return
// n2&#xFF1A;tFunc&#xFF0C;opt&#xFF0C;result&#xFF0C;1
// N1&#xFF1A; <span class="hljs-keyword">function&#xFF0C;<span class="hljs-built_in">let&#xFF0C;=&#xFF0C;+&#xFF0C;<span class="hljs-built_in">return
// N2&#xFF1A;tFunc&#xFF0C;opt&#xFF0C;result&#xFF0C;opt&#xFF0C;1&#xFF0C;result
// Vocabulary = n1 + n2 = 9
// length = N1 + N2 = 11
// Volume =  length * Log2 Vocabulary = 34.869<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span>

圈复杂度(Cyclomatic complexity,简写CC)也称为条件复杂度,是一种代码复杂度的衡量标准。由托马斯·J·麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度,其符号为VG或是M。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和 维护。程序的可能错误和高的圈复杂度有着很大关系。

如何计算

如果在控制流图中增加了一条从终点到起点的路径,整个流图形成了一个闭环。圈复杂度其实就是在这个闭环中线性独立回路的个数。

如图,线性独立回路有:

  • e1→ e2 → e
  • e1 → e3 → e

所以复杂度为2
对于简单的图,我们还可以数一数,但是对于复杂的图,这种方法就不是明智的选择了。

计算公式

V(G) = e &#x2013; n + 2 * p<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span>
  • e:控制流图中边的数量(对应代码中顺序结构的部分)
  • n:代表在控制流图中的判定节点数量,包括起点和终点(对应代码中的分支语句)
  • ps:所有终点只计算一次,即使有多个 return 或者 throw
  • p:独立组件的个数

几种常见的语句控制流图

一个例子

code

<span class="hljs-keyword">function <span class="hljs-built_in">test(index, string) {
       <span class="hljs-built_in">let <span class="hljs-built_in">returnString;
       <span class="hljs-keyword">if (index == 1) {
           <span class="hljs-keyword">if (string.length < 2) {
              <span class="hljs-built_in">return <span class="hljs-string">'&#x5206;&#x652F;1';
           }
           <span class="hljs-built_in">returnString = <span class="hljs-string">"returnString1";
       } <span class="hljs-keyword">else <span class="hljs-keyword">if (index == 2) {
           <span class="hljs-keyword">if (string.length < 5) {
              <span class="hljs-built_in">return <span class="hljs-string">'&#x5206;&#x652F;2';
           }
           <span class="hljs-built_in">returnString = <span class="hljs-string">"returnString2";
       } <span class="hljs-keyword">else {
          <span class="hljs-built_in">return  <span class="hljs-string">'&#x5206;&#x652F;3'
       }
       <span class="hljs-built_in">return <span class="hljs-built_in">returnString;
}<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

flow-chart

flow-chart

flow-graph

flow-graph

e&#xFF08;&#x8FB9;&#xFF09;&#xFF1A;9
n&#xFF08;&#x5224;&#x5B9A;&#x8282;&#x70B9;&#xFF09;&#xFF1A;6
p&#xFF1A;1
V = e - n + 2 * p = 5<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span>

如何优化

主要针对圈复杂度

大方向:减少判断分支和循环的使用

(下面某些例子可能举的不太恰当,仅用以说明这么一种方法)

// &#x4F18;&#x5316;&#x524D;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;4
<span class="hljs-keyword">function a (<span class="hljs-built_in">type) {
    <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'name') {
        <span class="hljs-built_in">return name:<span class="hljs-variable">${type};
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'age') {
        <span class="hljs-built_in">return age:<span class="hljs-variable">${type};
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'sex') {
        <span class="hljs-built_in">return sex:<span class="hljs-variable">${type};
    }
}

// &#x4F18;&#x5316;&#x540E;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;1
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">getName () {
    <span class="hljs-built_in">return name:<span class="hljs-variable">${type};
}
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">getAge () {
    <span class="hljs-built_in">return age:<span class="hljs-variable">${type};
}
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">getSex () {
    <span class="hljs-built_in">return sex:<span class="hljs-variable">${type};
}<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
// &#x4F18;&#x5316;&#x524D;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;4
<span class="hljs-keyword">function a (<span class="hljs-built_in">type) {
    <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'name') {
        <span class="hljs-built_in">return <span class="hljs-string">'Ann';
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'age') {
        <span class="hljs-built_in">return 11;
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (<span class="hljs-built_in">type === <span class="hljs-string">'sex') {
        <span class="hljs-built_in">return <span class="hljs-string">'female';
    }
}

// &#x4F18;&#x5316;&#x540E;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;1
<span class="hljs-keyword">function a (<span class="hljs-built_in">type) {
    <span class="hljs-built_in">let obj = {
        <span class="hljs-string">'name': <span class="hljs-string">'Ann',
        <span class="hljs-string">'age': 11,
        <span class="hljs-string">'sex': <span class="hljs-string">'female'
    };
    <span class="hljs-built_in">return obj[<span class="hljs-built_in">type];
}<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
// &#x4F18;&#x5316;&#x524D;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;4
<span class="hljs-keyword">function a (num) {
    <span class="hljs-keyword">if (num === 0) {
        <span class="hljs-built_in">return 0;
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (num === 1) {
        <span class="hljs-built_in">return 1;
    } <span class="hljs-keyword">else <span class="hljs-keyword">if (num === 2) {
        <span class="hljs-built_in">return 2;
    } <span class="hljs-keyword">else {
        <span class="hljs-built_in">return 3;
    }
}

// &#x4F18;&#x5316;&#x540E;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;2
<span class="hljs-keyword">function a (num) {
    <span class="hljs-keyword">if ([0,1,2].indexOf(num) > -1) {
        <span class="hljs-built_in">return num;
    } <span class="hljs-keyword">else {
        <span class="hljs-built_in">return 3;
    }
}<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
// &#x4F18;&#x5316;&#x524D;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;4
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">a () {
    <span class="hljs-built_in">let str = <span class="hljs-string">'';
    <span class="hljs-keyword">for (<span class="hljs-built_in">let i = 0; i < 10; i++) {
        str += <span class="hljs-string">'a' + i;
    }
    <span class="hljs-built_in">return str
}
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">b () {
    <span class="hljs-built_in">let str = <span class="hljs-string">'';
    <span class="hljs-keyword">for (<span class="hljs-built_in">let i = 0; i < 10; i++) {
        str += <span class="hljs-string">'b' + i;
    }
    <span class="hljs-built_in">return str
}
<span class="hljs-keyword">function <span class="hljs-function"><span class="hljs-title">c () {
    <span class="hljs-built_in">let str = <span class="hljs-string">'';
    <span class="hljs-keyword">for (<span class="hljs-built_in">let i = 0; i < 10; i++) {
        str += <span class="hljs-string">'c' + i;
    }
    <span class="hljs-built_in">return str
}

// &#x4F18;&#x5316;&#x540E;&#xFF0C;&#x5708;&#x590D;&#x6742;&#x5EA6;2
<span class="hljs-keyword">function a (<span class="hljs-built_in">type) {
    <span class="hljs-built_in">let str = <span class="hljs-string">'';
    <span class="hljs-keyword">for (<span class="hljs-built_in">let i = 0; i < 10; i++) {
        str += <span class="hljs-built_in">type + i;
    }
    <span class="hljs-built_in">return str
}<span class="copy-code-btn">&#x590D;&#x5236;&#x4EE3;&#x7801;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

首页项目总览

详情页-总览

详情页-代码复杂度检测详情

详情页-代码复杂度检测详情

详情页-代码复杂度检测详情

Original: https://www.cnblogs.com/0616–ataozhijia/p/11607977.html
Author: demoblog
Title: [代码质量] 代码质量管控 — 复杂度检测 (JavaScript)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/535088/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球