2013-03-13 41 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 - 回報