具有多个出口点的代码片段中的Cyclomatic Complexity

我有这个validation密码的方法:

/** * Checks if the given password is valid. * * @param password The password to validate. * @return {@code true} if the password is valid, {@code false} otherwise. */ public static boolean validatePassword(String password) { int len = password.length(); if (len  20) return false; boolean hasLetters = false; boolean hasDigits = false; for (int i=0; i<len; i++) { if (!Character.isLetterOrDigit(password.charAt(i))) return false; hasDigits = hasDigits || Character.isDigit(password.charAt(i)); hasLetters = hasLetters || Character.isLetter(password.charAt(i)); } return hasDigits && hasLetters; } 

让我们关注圈复杂度数:它的价值是什么?

度量1.3.6表示它是7,但我找不到七条独立的路径:我只找到5条! 维基百科没有多大帮助 – 我想如何使用这个公式π - s + 2

我有2个if ,1个和3个退出点,但我卡住了:我是否必须计算入口点? 因为它有两个条件,我应该计算第一个两倍吗?

编辑:

好的,现在我发现Cyclomatic Number是7.这意味着有7个独立的路径,所以如果我要覆盖100%的代码,我应该能够找到7个不同的测试用例,对吗?

好吧,我还是找不到最后一个! 我找到了这些:

  1. 有效:asdf1234
  2. 太短了:asdf123
  3. 太久了:asdfsgihzasweruihioruldhgobaihgfuiosbhrbgtadfhsdrhuorhguozr
  4. 字符无效:asdf * 123
  5. 全位数:12345678
  6. 无数字:asdfghjk
  7. 跆拳道???

我认为诀窍在于计算逻辑运算符。

基于McCabe Cyclomatic Complexity部分下的Metrics链接( http://metrics.sourceforge.net/ ):

1初始流程

3个决策点(if,for,if)

3个条件逻辑运算符(||,||,||)

总数:7

我认为这里的主要问题是条件执行短路,这是一种控制流程。 有用的是重新编写代码以使其明确。 在进行程序分析时,这种标准化很常见。 一些特殊的规范化(不是正式的,机器不能生成这个,但它得到了重点)会使你的代码如下所示:

 public static boolean validatePassword(String password) { int len = password.length(); //evaluate 'len < 8 || len > 20' bool cond1 = len < 8; if (!cond1) cond1 = len > 20; //do the if test if (cond1) return false; boolean hasLetters = false; boolean hasDigits = false; //for loops are equivalent to while loops int i = 0; while(i < len) { if (!Character.isLetterOrDigit(password.charAt(i))) return false; //evaluate 'hasDigits || Character.isDigit(password.charAt(i))' bool hasDigitsVal = hasDigits; if (!hasDigitsVal) hasDigitsVal = Character.isDigit(password.charAt(i)); //hasDigits = ... hasDigits = hasDigitsVal //evaluate 'hasLetters || Character.isLetter(password.charAt(i))' bool hasLettersVal = hasLetters; if (!hasLettersVal) hasLettersVal = Character.isLetter(password.charAt(i)); //hasLetters = ... hasLetters = hasLettersVal; i++; } //evaluate 'hasDigits && hasLetters' bool cond2 = hasDigits; if (cond2) cond2 = hasLetters; //return ... return cond2; } 

请注意||&&运算符基本上只是将if语句添加到代码中。 另请注意,您现在有6个if语句和一个while循环! 也许这就是你要找的7?


关于多个出口点,这是一个红鲱鱼。 将每个函数视为具有一个退出节点,即函数的结尾。 如果您有多个return语句,则每个return语句都会为该出口节点绘制一条边。

 void foo() { if (cond1) return a; if (cond2) return b; return c; } 

图形看起来像这样,其中-----val----> EXIT表示退出函数的值为val

 START -> cond1 ------------------------a------------> EXIT | | cond2 ------------------------b----------------+ | | return -----------------------c----------------| 

如果您重新编写代码,那么您基本上只需添加另一个“预返回”节点,然后转到退出节点:

 void foo() { int val; if (cond1) { val= a; } else { if (cond2) { val= b; } else { val= c; } } return val; } 

现在它看起来像这样:

 START -> cond1 ---> val=a --------------------------> return ----val----> EXIT | | cond2 ---> val=b ------------------------------+ | | + -----> val=c ------------------------------+ 

它仍然很复杂,而且代码更加丑陋。

正如在这里很好地解释:

Cyclomatic Complexity =(2 + ifs + loops + cases – return)其中:

 * ifs is the number of IF operators in the function, * loops is the number of loops in the function, * cases is the number of switch branches in the function (without default), and * return is the number of return operators in the function. 

如前所述,还计算了逻辑条件。

例如, if (len < 8 || len > 20)计为3个条件:

  1. if
  2. len<8
  3. len > 20

这意味着,您的代码的复杂度为2 + 8 - 3 = 7 ,其中:

  • 2 - 它总是在那里(见那里的配方)
  • 8 - 分支数量
  • 3 - 退货数量