代码的复杂度是评估一个项目的重要标准之一。较低的复杂度既能减小项目的维护成本,又能避免一些不可控问题的出现。然而在平常的开发中却没有一个明确的标准去衡量代码结构的复杂程度,你们只能凭着经验去评估代码结构的复杂程度,好比,代码的程度、结构分支的多寡等等。当前代码的复杂度究竟是个什么水平?何时就须要咱们去优化代码结构、下降复杂度?这些问题咱们不得而知。
所以,咱们须要一个明确的标准去衡量代码的复杂度。git
Litmus
是咱们团队建设的一个代码质量检测系统,目前包括代码的风格检查、重复率检查以及复杂度检查。litmus 采用代码的 Maintainability(可维护性)来衡量一个代码的复杂度,而且经过如下三个方面来定义一段代码的 Maintainability 的值:es6
根据这三个参数计算出 Maintainability,也就是代码的可维护性,公式以下:github
Maintainability Index = MAX(0,(171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171)复制代码
代码行数不作赘述,下面咱们具体介绍代码容量、圈复杂的含义以及它们的计算原理npm
代码的容量关注的是代码的词汇数,有如下几个基本概念bash
参数 | 含义 |
---|---|
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,容量 |
一个例子函数
function tFunc(opt) {
let result = opt + 1;
return result;
}
// n1:function,let,=,+,return
// n2:tFunc,opt,result,1
// N1: function,let,=,+,return
// N2:tFunc,opt,result,opt,1,result
// Vocabulary = n1 + n2 = 9
// length = N1 + N2 = 11
// Volume = length * Log2 Vocabulary = 34.869复制代码
圈复杂度(Cyclomatic complexity,简写CC)也称为条件复杂度,是一种代码复杂度的衡量标准。由托马斯·J·麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度,其符号为VG或是M。它能够用来衡量一个模块断定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖全部的可能状况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和 维护。程序的可能错误和高的圈复杂度有着很大关系。工具
因此复杂度为2
对于简单的图,咱们还能够数一数,可是对于复杂的图,这种方法就不是明智的选择了。测试
V(G) = e – n + 2 * p复制代码
一个例子优化
codeui
function test(index, string) {
let returnString;
if (index == 1) {
if (string.length < 2) {
return '分支1';
}
returnString = "returnString1";
} else if (index == 2) {
if (string.length < 5) {
return '分支2';
}
returnString = "returnString2";
} else {
return '分支3'
}
return returnString;
}复制代码
flow-chart
e(边):9
n(断定节点):6
p:1
V = e - n + 2 * p = 5复制代码
主要针对圈复杂度
大方向:减小判断分支和循环的使用
(下面某些例子可能举的不太恰当,仅用以说明这么一种方法)
// 优化前,圈复杂度4
function a (type) {
if (type === 'name') {
return `name:${type}`;
} else if (type === 'age') {
return `age:${type}`;
} else if (type === 'sex') {
return `sex:${type}`;
}
}
// 优化后,圈复杂度1
function getName () {
return `name:${type}`;
}
function getAge () {
return `age:${type}`;
}
function getSex () {
return `sex:${type}`;
}复制代码
// 优化前,圈复杂度4
function a (type) {
if (type === 'name') {
return 'Ann';
} else if (type === 'age') {
return 11;
} else if (type === 'sex') {
return 'female';
}
}
// 优化后,圈复杂度1
function a (type) {
let obj = {
'name': 'Ann',
'age': 11,
'sex': 'female'
};
return obj[type];
}复制代码
// 优化前,圈复杂度4
function a (num) {
if (num === 0) {
return 0;
} else if (num === 1) {
return 1;
} else if (num === 2) {
return 2;
} else {
return 3;
}
}
// 优化后,圈复杂度2
function a (num) {
if ([0,1,2].indexOf(num) > -1) {
return num;
} else {
return 3;
}
}复制代码
// 优化前,圈复杂度4
function a () {
let str = '';
for (let i = 0; i < 10; i++) {
str += 'a' + i;
}
return str
}
function b () {
let str = '';
for (let i = 0; i < 10; i++) {
str += 'b' + i;
}
return str
}
function c () {
let str = '';
for (let i = 0; i < 10; i++) {
str += 'c' + i;
}
return str
}
// 优化后,圈复杂度2
function a (type) {
let str = '';
for (let i = 0; i < 10; i++) {
str += type + i;
}
return str
}复制代码
npm install --save es6-plato
es6-plato -r -d report ./复制代码
该系统由咱们团队开发,目前仅限美团点评公司内部使用
,系统部分截图以下