白盒测试之圈复杂度,以及可以直接降低圈复杂度的10种重构技术

元气小坏坏 提交于 2019-12-08 11:02:33

圈复杂度

圈复杂度(Cyclomatic Complexity)是一种代码复杂度的衡量标准。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和维护。程序的可能错误和高的圈复杂度有着很大关系。

下面这个实例中,单元测试的覆盖率可以达到100%,但是很容易发现这其中已经漏掉了一个NPE的测试用例。case1方法的圈复杂度为2,因此至少需要2个用例才能完全覆盖到其所有的可能情况。

    //程序原代码,圈复杂度为 2

public String case1(int num) {

       String string = null;

if (num == 1) {

           string = "String";

       }

return string.substring(0);

    }

//上面代码的单元测试代码

public void testCase1(){

       String test1 = case1(1);

    }

圈复杂度主要与分支语句(if、else、,switch 等)的个数成正相关。可以在图1中看到常用到的几种语句的控制流图(表示程序执行流程的有向图)。当一段代码中含有较多的分支语句,其逻辑复杂程度就会增加。在计算圈复杂度时,可以通过程序控制流图方便的计算出来。通常使用的计算公式是V(G) = e – n + 2 , e 代表在控制流图中的边的数量(对应代码中顺序结构的部分),n 代表在控制流图中的节点数量,包括起点和终点(1、所有终点只计算一次,即便有多个return或者throw;2、节点对应代码中的分支语句)。

图1、各判断语句的控制流图

知道了如何计算圈复杂度,我们来使用控制流图重新计算一次case1方法的圈复杂度,其控制流图如下图。状态1表示if(num == 1 )的条件判断,状态2表示string=”String”的赋值操作。可以通过下面的控制流图得到 e = 3 ; n = 3;那么全复杂度V(G) = 3 - 3 + 2 = 2,既case1的圈复杂度为2。

图2、case1的控制流图

在看一个计算全复杂度的例子。程序代码如下:

public String case2(int index, String string) {

       String returnString = null;

if (index < 0) {

throw new IndexOutOfBoundsException("exception <0 ");

       }

if (index == 1) {

if (string.length() < 2) {

return string;

           }

           returnString = "returnString1";

       } else if (index == 2) {

if (string.length() < 5) {

return string;

           }

           returnString = "returnString2";

       } else {

throw new IndexOutOfBoundsException("exception >2 ");

       }

return returnString;

    }

程序控制流图:

图3、case2的控制流图

根据公式 V(G) = e – n + 2 = 12 – 8 + 2 = 6 。case2的圈复杂段为6。说明一下为什么n = 8,虽然图上的真正节点有12个,但是其中有5个节点为throw、return,这样的节点为end节点,只能记做一个。

在开发中常用的检测圈复杂度的工具,PMD,checkstyle都可以检测到高复杂度的代码块。在代码的开发中,配合各种圈复杂度的检测插件,将高复杂度的代码进行适当的拆分、优化,可以大大提高代码整体的质量,减少潜在bug存在。

 

可以直接降低圈复杂度的9种重构技术(针对结构化编程):

•Composing Methods(重新组织你的函数)

Extract Method(提炼函数)

Substitute Algorithm(替换你的算法)

•Simplifying Conditional Expressions(简化条件表达式)

Decompose Conditional(分解条件式)

Consolidate Conditional Expression(合并条件式)

Consolidate Duplicate Conditional Fragments(合并重复的条件片断)

Remove Control Flag(移除控制标记)

•Making Method Calls Simpler(简化函数调用)

Separate Query from Modifier(将查询函数和修改函数分离)

Parameterize Method(令函数携带参数)

Replace Parameter with Explicit Methods(以明确函数取代参数)

针对面向对象编程:

Replace Conditional with Polymorphism(以多态取代条件式)

 

①Extract Method(提炼函数)

②Substitute Algorithm(替换你的算法)

③Decompose Conditional(分解条件式)

④Consolidate Conditional Expression(合并条件式)

⑤Consolidate Duplicate Conditional Fragments(合并重复的条件片断)

⑥Remove Control Flag(移除控制标记)

⑦Separate Query from Modifier(将查询函数和修改函数分离)

⑧Parameterize Method(令函数携带参数)

image

⑨Replace Parameter with Explicit Methods(以明确函数取代参数)

⑩Replace Conditional with Polymorphism(以多态取代条件式)

image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!