圈复杂度Cyclomatic complexity

一、什么是圈复杂度

        圈复杂度(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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值