概述
一、什么是圈复杂度
圈复杂度(Cyclomatic complexity,简写 CC)也称为条件复杂度,是模块结构复杂度的度量,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数。
成立于1976年的McCabe&Associates公司开发出了McCabe Cyclomatic Complexity Metric(圈复杂度)技术。Metric以软件复杂度测量的数目为基础,能帮助工程师识别难于测试和维护的模块,圈复杂度已经成为评估软件质量的一个重要标准。人们可以用圈复杂度对软件的复杂度和质量进行衡量,来安排工程进度,在成本、进度和性能之间寻求平衡。
研究表明,程序的Cyclomatic复杂性与其可维护性和可测试性之间存在相关性,这意味着对于更高复杂性的文件,在修复、增强或重构源代码时出错的概率更高。但是要注意,McCabe度量数大的程序,不见得结构化就不好。例如,Case语句是良结构的,但可能有很大的McCabe度量数(依赖于语句中的分支数),这可能是由于问题和解决方案所固有的复杂性所决定的。使用者应当自己决定如何使用McCabe度量所提供的信息。
二、如何计算圈复杂度
圈复杂度Cyclomatic complexity =(1 + ifs + loop + case),其中
ifs - number of if, else if,else statements in the current function
loop - number of for, while, and do-while statements in the current function
case - the number of switch branches in the function (without default)
要注意的是,人工计算cc时要包含宏展开之后的代码中的if、case等语句。
void FDL_AnalyEraseCmd(void)
{
U08 bNode, PtStat;
bNode = gtFdlDrvSubmQ.tCmdClassLink.tLinkArg[FDL_CMD_CLASS_ERASE].bHead;
while (bNode != INVALID_U8) //有效节点,则说明有有效命令需要处理
{
if (FALSE == gtFdlDrvSubmQ.bCfg2FcFlg[bNode])
{
YS_ASSERT((FDL_DRV_SEG_HEAD_TAIL == gtFdlDrvSubmQ.sDrvCmd[bNode].bSegFlg), "FDL_AnalyEraseCmd-->err1");
YS_ASSERT((FDL_DRV_CMD_ERASE == gtFdlDrvSubmQ.sDrvCmd[bNode].bCmd), "FDL_AnalyEraseCmd-->err2");
PtStat = Thread_FDL_EraseCmd(&PT_PrcsErsCmd[0], bNode);
if (PT_ENDED != PtStat)
{
break;
}
}
bNode = gtFdlDrvSubmQ.tCmdClassLink.bLinkNext[bNode];
}
}
上述代码的圈复杂度为 1+2+1+0=4。
拓展概念:
Extended cyclomatic complexity:在圈复杂度的计数基础上加上逻辑布尔运算符。每当Klocwork在条件语句中找到逻辑布尔运算符(&&或|)时,EXTCYCLOMATIC就会增加1。
Plain cyclomatic complexity:类似于圈复杂度,但是在预处理器扩展之前计算,从宏定义生成的任何条件语句都不会生成PLAINCYCLOMATIC度量。
Plain extended cyclomatic complexity :类似于扩展圈复杂度,但在预处理器扩展之前计算,从宏定义生成的任何条件语句都不会生成此度量。
三、圈复杂度阈值
Cyclomatic Complexity | Risk Evaluation |
1-10 | 一个没有太大风险的简单模块 |
11-20 | 具有中等风险的更复杂模块 |
21-50 | 高风险的复杂模块 |
51 and greater | 风险极高的不稳定项目 |
四、如何降低圈复杂度
分两个方向降低圈复杂度,一是拆分函数,二是尽量减少if、else、while、case等这些流程控制语句。
4.1 拆分函数
圈复杂度的计算范围是在一个function内的,将业务代码拆分成一个一个的职责单一的小函数,如此除了能够降低圈复杂度,也能提高代码的可读性和可维护性。
4.2 减少流程控制
1. 减少不必要条件、循环分支,尽量少用 if …else … ,采用三元表达式替换 if else;
if (DMAINFO_ABNORMAL(wDmaSts))
{
bRetryType = RD_ERR_DATA_ERROR; /*软解Fail*/
}
else
{
bRetryType = RD_ERR_SOFT_PASS; /*软解Pass*/
}
//修改后:
bRetryType = (DMAINFO_ABNORMAL(wDmaSts)) ? RD_ERR_DATA_ERROR : RD_ERR_SOFT_PASS;
2. 合并条件表达式,比如使用 a || b || c;
3. 去掉没有必要的else
if (false)
{
return;
}
else
{
c = a;
}
//修改后:
if (false)
{
return;
}
c = a;
4. 同一条件多处出现,重构函数
if (b)
{
if (a)
{
Func1();
}
}
else
{
if (a)
{
Func2();
}
}
if (a)
{
Func3();
}
//修改后:
if (a)
{
if (b)
{
Func1();
}
else
{
Func2();
}
Func3();
}
5. 未完待续;
注:以上圈复杂度计算与复杂度分级均基于klocwork平台的metric。
最后
以上就是开心镜子为你收集整理的圈复杂度Cyclomatic complexity一、什么是圈复杂度二、如何计算圈复杂度三、圈复杂度阈值四、如何降低圈复杂度的全部内容,希望文章能够帮你解决圈复杂度Cyclomatic complexity一、什么是圈复杂度二、如何计算圈复杂度三、圈复杂度阈值四、如何降低圈复杂度所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复