多次调用相同方法时的圈复杂度


12

多亏了Code Review的一个问题,我对以下代码的确切复杂度有一点分歧(这本质上是一个学习的机会)。

public static void main(String[] args) {
    try {
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
    }
    catch (NullPointerException e) {
    }
}

private static Random random = new Random();

public static void thro() throws NullPointerException {
    if (random.nextBoolean())
        throw new NullPointerException();
    System.out.println("No crash this time");
}

在Eclipse中使用Eclipse指标插件编写此代码时,它告诉我main方法的McCabe Cyclomatic Complexity为2,thro方法为2。

但是,其他人告诉我,thro多次调用的复杂度为number of calls * method complexity,因此声称main方法的复杂度为7 * 2 = 14。

我们在衡量不同的事物吗?我们俩可以正确吗?还是这里实际的圈复杂度是多少?


5
功能的CC 为2,因为只有两条路径通过。程序的CC 更高。这是黑暗中的一个完整问题,但是我认为代码分析软件由于无法一次性计算整个复杂应用程序的CC,因此将每个功能都当作一个单独的黑匣子。
Phoshi

@Phoshi如果您将其写为答案,并(如果可能)提供显示两者分开的链接,我很乐意接受该答案。
西蒙·佛斯伯格

如果算上在CC测量造成的可能的例外的所有路径,愿上帝保佑谁问这个问题上重构了一些琐碎的代码,以获得10岁以下人数的家伙
mattnz

Answers:


9

当我理解这个正确的圈复杂main是8 -那就是通过代码线性独立的路径的数目。您会在七行之一中得到一个例外,或者一无所获,但不要超过一个。每个可能的“例外点”都完全对应于代码中的一条不同路径。

我猜想当McCabe发明该度量标准时,他没有考虑异常处理的编程语言。


但是,哪一行引发异常真的重要吗?
西蒙·福斯伯格

5
@SimonAndréForsberg:是的,确实如此。考虑“ thro”的副作用,即在调用它时会增加全局计数器的值(不会更改通过代码的可能路径)。该计数器的可能结果是,然后0至7,所以这证明了CC是至少 8
Brown博士

您是否会说我正在使用的指标插件报告的方法值不正确main
西蒙·佛斯伯格

@SimonAndréForsberg:好吧,我不知道您的指标插件,但是2显然不是8。–
Doc Brown

还有的指标链接插件在我的问题....
西蒙·福斯贝里

6

作为“另一个人”,我将在这里回答,并且要对我说的话保持精确(在其他形式上,我并不是特别精确)。

使用上面的代码示例,我将圈复杂度计算为8,并且在代码中有注释,以显示如何计算该复杂度。为了描述的路径我会考虑成功遍历所有thro()呼叫作为“主”“代码路径”(或“CP = 1”):

public static void main(String[] args) {
  try {
             // This is the 'main' Code Path: CP = 1
    thro();  // this has a branch, can succeed CP=1 or throw CP=2
    thro();  // this has a branch, can succeed CP=1 or throw CP=3
    thro();  // this has a branch, can succeed CP=1 or throw CP=4
    thro();  // this has a branch, can succeed CP=1 or throw CP=5
    thro();  // this has a branch, can succeed CP=1 or throw CP=6
    thro();  // this has a branch, can succeed CP=1 or throw CP=7
    thro();  // this has a branch, can succeed CP=1 or throw CP=8
  }
  catch (NullPointerException e) {
  }
}

因此,在这种主要方法中,我计算了8条代码路径,对我来说,其循环复杂度为8。

用Java术语来说,每种退出函数的机制都取决于它的复杂性,因此,一种具有成功状态并抛出(例如最多可能引发3个异常)方法的方法中,有4条记录了退出路径。

调用此类函数的方法的复杂度为:

CC(method) = 1 + sum (methodCallComplexity - 1)

我想其他的事情要考虑,就是,在我看来,该catch条款也不会有助于该方法的复杂性,catch仅仅是一个目标throws分支,因此catch块是多重的目标throw小号计数1次对于每个throw,而不仅仅是对所有内容一次。


您是否也在计算OutOfMemoryExceptions的可能分支?我的意思是说,它们会导致代码分支,但没人认为它们会稀释代码的有用性。
Telastyn 2013年

不,我不是...而您是对的,但是,在此参数的上下文中,我仅计算声明该方法抛出的异常。另外,如果一个方法声明了三个异常,但callinch代码却做了一个,catch (Throwable t) {...那么我猜它声明要抛出多少个异常都没有关系。
rolfl 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.