2013-03-13 79 views
5

一段代码,我有这样的方法验证密码:圈复杂度与多个出口

/** 
* 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 < 8 || 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; 
} 

让我们专注于圈复杂度号码:这是它的价值?

Metrics 1.3.6说这是7,但我真的找不到七个独立的路径:我只找到5个! Wikipedia没有太多帮助—我想如何使用这个公式π - s + 2

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

编辑:

好了,现在我发现,圈数是7,这意味着,有7条独立的路径,所以我应该可以,如果我愿意支付100找到7种不同的测试案例%的代码,我说得对吗?

嗯,我还是找不到最后一个! 我发现这些:

  1. 有效期:asdf1234
  2. 太短:asdf123
  3. 太长:asdfsgihzasweruihioruldhgobaihgfuiosbhrbgtadfhsdrhuorhguozr
  4. 无效字符:ASDF * 123
  5. 所有位数:12345678
  6. 否 - 数字:asdfghjk
  7. wtf ???
+0

看在我的答案归一化代码的数量 - 分支

  • 3的数量。考虑到'||'和'&&'短路后,你有7条分支语句 – Claudiu 2013-03-13 18:01:11

  • +1

    第7条分支是'for'循环终止的时候。一条路径进入for循环,另一条路径退出。你从来没有在你的代码中运行'for'循环的情况,因为你在函数启动时检查了'len <8',但是简单的自动代码分析并不能反映出来。 – Claudiu 2013-03-13 18:02:58

    +0

    @Claudiu:我认为可能是这样,但我不确定......我的疑问是:为什么'for'需要两次计数,其中'if'和其他所有计数只计算一次?即使“if”陈述有两种方式,但只能通过一种方式递增ccn,因此也应该执行“for”。 :| – tmh 2013-03-13 18:07:53

    回答

    3

    我想诀窍是逻辑运算符被计算在内。

    基于断下的麦凯布圈复杂部分的度量链路(http://metrics.sourceforge.net/)的:

    1初始流动

    3决策点(如果,如果)

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

    总:7

    +0

    顺便说一句,逻辑运营商被视为分支,因为他们有[短路行为](http://en.wikipedia.org/wiki/Short-circuit_evaluation)。例如,当“||”运算符左侧的表达式评估为“真”时,右侧不执行。 – matts 2013-03-13 17:31:26

    +1

    我想你的权利,但我想知道......这是正确的?那么这段代码真正的圈数是多少?指标是否正确?所有这一切的重点是涵盖所有可能的测试路径。编辑:想了两次,我发现指标是正确的;我没有考虑两个测试用例:全数字密码和没有任何数字的密码。顺便说一句,我认为最后&&不增加ccn:你没有指望主流量:) – tmh 2013-03-13 17:35:42

    +0

    你是对的。回答编辑显示此。这似乎有很多方法来计算,通常最终是相同的总数:http://stackoverflow.com/questions/10365912/mccabes-cyclomatic-complexity – Shellum 2013-03-13 17:43:47

    2

    我认为这里的最主要的是,条件语句做短线慈rcuiting,这是一种控制流的形式。有什么帮助的是重写代码来明确。进行程序分析时,这种标准化是常见的。一些临时正常化(不正规和一台机器就不会产生这一点,但它横跨得到点)将让你的代码如下所示:

    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 ------------------------------+ 
    

    它仍然是复杂的,代码只是丑陋的。

    +2

    “我不会说它有多个出口点”你是什么意思?我习惯于每次返回时添加一个退出点(例如'if(somecondition)return;') – tmh 2013-03-13 17:43:51

    +0

    @tmh:我更新了我的答案以显示我的意思。考虑多个'return'语句是一个红鲱鱼国际海事组织 – Claudiu 2013-03-13 17:58:46

    0

    作为很好地解释here

    圈复杂=(2 + IFS +环路+案例 - 返回)其中:

    * 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 - 回报