关于圈/环复杂度

关于圈/环复杂度

圈/环复杂度(Cyclomatic complexity)是一种代码复杂度的衡量标准。其由托马斯·J·麦凯布(Thomas J. McCabe, Sr.)于1976年提出,用来表示程序的复杂度。它可以用来衡量一个模块判定结构的复杂程度,也可以理解为覆盖所有可能情况所需的最少测试用例数。圈/环复杂度大说明程序代码的判断逻辑复杂,可维护性不好。程序的可能错误和高的圈复杂度有着很大关系。


圈/环复杂度的计算方法:

  1. 点边计算法:V(G)=E-N+2
  2. 节点判定法:V(G)=P+1

E:控制流图中边的数量 N:控制流图中节点的数量 P:判定节点数量

控制流图:

控制流图是程序流程图的一种简化,它可以更加突出的表示程序控制流的结构。控制流图只有节点和边两种简单的图示符号。

  1. 节点:表示基本的程序块,可以是一个单独的语句,也可以是多个顺序执行的语句构成的语句块。

  2. 判定节点:是节点的一种,是带有分支的节点,典型例子为if语句对应的节点。当判定条件为复合条件时,需要将符合条件分解为简单条件,构成多个判定节点。比如会被分解为

    判定节点具体包括:if/while/for/case/catch语句、bool操作、?:三元运算符等。

  3. 边:连接相关的两个节点


圈/环复杂度与可维护性

圈/环复杂度更高的模块和方法,其缺陷个数可能会更多,会导致维护成本上升,可维护性下降。

圈/环复杂度 代码状况 可测性 维护成本
1~10 清晰 结构化
10~20 复杂
20~30 非常复杂
>30 不可读 不可测 非常高

利用圈/环复杂度

  1. 在编写代码时,阶段性检查圈/环复杂度。如果圈/环复杂度并不理想,要及时进行优化,降低圈/环复杂度。这样一方面可以提高代码的可维护性,便于后期代码编写、使用和维护。另一方面可以“在缺陷成为缺陷之前捕获它们”,减少潜在问题,及时止损。
  2. 圈/环复杂度可以为设计测试用例提供一个很好的参考。好的经验是:创建数量与被测代码圈复杂度值相等的测试用例,以此提升用例对代码的分支覆盖率。
  3. 在维护或扩展时,为提升代码质量,可以使用圈/环复杂度作为切入点。结合控制流图的直观展示,可以更明确优化位置。

降低圈/环复杂度的方法

  1. 提炼函数:一段代码可以被组织在一起并独立出来,放进一个独立函数中。尤其是针对常被复用的代码片段。
  2. 替换算法:把某个算法替换为另一个更清晰的算法。设计算法时应针对问题明确思路。在Java可以充分利用丰富的类库简化算法的某些步骤。
  3. 简化条件表达式:
    1. 逆向表达:先写出基本的判断语句,如果条件表达式比较复杂,考虑条件表达式取反是否简单,如果条件表达式取反易于表达,则可以调换表达顺序。
    2. 分解条件:提炼出独立的函数,以函数名明确判断意图。
    3. 合并条件:一系列条件判断结果相同时,合并这些条件表达式到一个独立函数中。
    4. 移除控制标记:减少使用bool类型作为逻辑控制标记,以break/continue/return等代替。
    5. 以多态取代条件式:针对switch-case,将整个条件式的每个分支放进一个子类的重载方法中,然后将原始函数声明为抽象方法。
  4. 简化函数调用
    1. 分离函数:将查询对象值与修改对象值的功能分离到两个不同的函数中。
    2. 参数化方法:如果多个函数/语句块执行相似的工作只是具体计算的值不同,则将计算的值参数化后建立单一函数执行对参数的计算。
    3. 以明确函数取代参数:当函数实现完全取决于参数值而采取不同反应时,针对该参数的每一个可能值,建立一个独立函数。人为设置的函数名让代码更容易被理解,而尝试通过参数名结合条件表达式理解函数会比较困难。

注意

  1. 高圈/环复杂度的代码不一定代表可维护性差。switch-case多重分支是一个典型例子,圈/环复杂度高但是逻辑比较明确。圈/环复杂度于可读性和可维护性的关系要辩证看待。
  2. 圈/环复杂度相同的代码其他属性不一定相同,可读性、可维护性也可能存在差异。这取决于代码的具体实现方式。

关于圈/环复杂度的感受

参考已有的blogs写完上面的部分后,发现通过圈/环复杂度评估代码是编写代码时的一种很好的方法。尤其是在写降低圈/环复杂度的方法部分时,其中的一些方法和对应的情况在之前练习编写代码时遇到过很多次,比如简化条件表达式和提炼函数等。使用这些方法确实能够很大程度上增强代码的可读性和可维护性。

结合圈/环复杂度和降低圈/环复杂度的方法重新审查了Lab1和Lab2中编写的代码。发现两次实验给出的TurtleSoup、Graph等框架很好地体现了简化函数调用中分离函数和参数化等思想,使得框架中每个方法在编写时更加容易,编写出的代码圈/环复杂度比较低。在Lab2中GraphPoet和FriendShipGraph类中的实现的一些方法圈/环复杂度较高,虽然仍处在<10范围内,但是可读性并不好,当时编写时也感到维护比较费劲。后续编写代码时应在编写过程中更多关注圈/环复杂度等和可维护性相关的判断指标,编写过程中阶段性及时进行优化。


参考

http://kaelzhang81.github.io/2017/06/18/详解圈复杂度/#圈复杂度和软件质量

https://blog.csdn.net/luoxuexi2020/article/details/117968559

posted @ 2022-06-05 11:30  Twinblade_i  阅读(982)  评论(0编辑  收藏  举报