2013-03-13 11 views
5

mam to metoda, która sprawdza się hasło: skupićZłożoność cykliczna w kawałek kodu z wieloma punktami wyjścia

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

Miejmy na cyclomatic liczby Złożoność: jaka jest jego wartość?

Metrics 1.3.6 mówi, że to 7, ale nie mogę znaleźć siedmiu niezależnych ścieżek: znajduję tylko 5! I Wikipedia nie pomogło zbytnio — jak mam użyć tej formuły π - s + 2?

Mam 2 if, 1 for i 3 punkty wyjścia, ale utknąłem: czy muszę liczyć punkt wejścia? Czy powinienem policzyć dwa razy pierwsze if, ponieważ ma on dwa warunki?

EDIT:

Ok, teraz okazało się, że numer jest Cyclomatic 7. Oznacza to, że istnieje 7 niezależnych ścieżek, a więc powinien być w stanie znaleźć 7 różnych przypadków testowych jeśli chciałbym na pokrycie 100 % kodu, czy mam rację?

Cóż, nadal nie mogę znaleźć ostatniego! znalazłem te:

  1. Ważny: asdf1234
  2. zbyt krótki: asdf123
  3. Too Long: asdfsgihzasweruihioruldhgobaihgfuiosbhrbgtadfhsdrhuorhguozr
  4. Nieprawidłowy znak: asdf * 123
  5. All-cyfrowy: 12345678
  6. no- cyfry: asdfghjk
  7. wtf ???
+0

spojrzeć na znormalizowanego kodu w mojej odpowiedzi. Po uwzględnieniu zwarcia '||' i '&&' masz 7 instrukcji rozgałęzień – Claudiu

+1

7. gałąź jest po zakończeniu pętli 'for'. Jedna ścieżka przechodzi w pętlę for, druga gaśnie. Nigdy nie masz przypadku, w którym pętla 'for' nie działa w twoim kodzie, ponieważ sprawdzasz' len <8' na początku swojej funkcji, ale prosta automatyczna analiza kodu tego nie odzwierciedli. – Claudiu

+0

@Claudiu: Myślałem, że to możliwe, ale nie byłem pewien ... moje wątpliwości są następujące: dlaczego "za" należy policzyć dwa razy, gdzie "jeśli" i wszystko inne liczy się tylko raz? Nawet instrukcja "if" ma dwukierunkowy przyrost, ale zwiększa wartość ccn tylko o jeden i tak samo powinno się robić 'for'. : | – tmh

Odpowiedz

3

Myślę, że lewę polegają na liczeniu operatorów logicznych.

oparciu off linku Metrics (http://metrics.sourceforge.net/) w sekcji McCabe Złożoność cykliczna:

1 przepływu Początkowa

3 punkty decyzji (jeśli na razie)

3 warunkowe operatorów logicznych (||, ||, ||)

sumie: 7

+0

btw, operatory logiczne są liczone jako rozgałęzienia, ponieważ mają [zachowanie zwarciowe] (http://en.wikipedia.org/wiki/Krótkie-przeniesienie_kurcze). np. gdy wyrażenie po lewej stronie operatora '||' ma wartość "prawda", prawa strona nie jest wykonywana. – matts

+1

Zgaduję, że masz rację, ale zastanawiam się ... czy to prawda? Jaka jest prawdziwa liczba cykliczna tego fragmentu kodu? Czy dane są prawidłowe? Celem tego wszystkiego jest pokrycie wszystkich możliwych ścieżek testowania. EDYCJA: myśląc dwa razy, dowiedziałem się, że dane są poprawne; Nie brałem pod uwagę dwóch przypadków testowych: całkowicie numerycznego hasła i hasła bez żadnej cyfry. Btw myślę, że ostatni && nie zwiększa ccn: nie policzyłeś głównego przepływu :) – tmh

+0

Masz rację. Odpowiedź edytowana, aby to pokazać. Wygląda na to, że istnieje wiele sposobów na zliczanie tego, że generalnie kończy się to na tej samej sumie: http://stackoverflow.com/questions/10365912/mccabes-cyclomatic-complexity – Shellum

2

Myślę, że najważniejsze jest to, że warunkowe zrobić krótkie-ci rcuiting, który jest formą przepływu sterowania. Pomocne jest ponowne napisanie kodu, aby było to wyraźne. Ten rodzaj normalizacji jest powszechny podczas wykonywania analizy programu. Niektóre normalizacja ad-hoc (nie formalny i maszyna nie będzie generować, ale robi się punkt w poprzek) stałaby kod wyglądać następująco:

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; 
} 

zauważyć, jak operatorzy || i && zasadniczo tylko dodać if wyciągi do kodu. Zauważ, że masz teraz 6 if instrukcji i jedną pętlę while! Może to 7, którego szukałeś?


O wielu punktach wyjścia, to jest czerwony śledź. Każda funkcja powinna mieć jeden węzeł wyjściowy, koniec funkcji. Jeśli masz wiele instrukcji return, każda instrukcja return rysuje krawędź do tego węzła wyjściowego.

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

wykres wyglądałby następująco, gdzie -----val----> EXIT oznacza wyjście z funkcji z wartością val:

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

Jeśli ponownie napisać kod, a następnie po prostu w zasadzie dodać kolejny „pre- zwrot”węzeł, który następnie przechodzi do węzła wyjścia:

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

teraz wygląda to tak:

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

Jest wciąż tak złożony, a kod jest po prostu brzydszy.

+2

"Nie powiedziałbym, że ma wiele punktów wyjścia" co masz na myśli? Kiedyś zwracałem uwagę, że za każdym razem, gdy zwracasz, dodajesz punkt wyjścia (np. 'If (somecondition) return;') – tmh

+0

@tmh: zaktualizowałem swoją odpowiedź, aby pokazać, co mam na myśli. myślenie o wielu zwrotach 'return' jest IMO z czerwonym śledziem – Claudiu

0

Jak dobrze wyjaśnione here:

Złożoność cykliczna = (2 + IFS + pętle + przypadkach - Return) gdzie:

* 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. 

Jak już wspomniano, stany logiczne są również obliczenia.

Dla przykładu if (len < 8 || len > 20) liczy się jako 3 warunki:

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

Oznacza to, że kod ma złożoność 2 + 8 - 3 = 7, gdzie:

  • 2 - zawsze jest tam (patrz wzór tam)
  • 8 - liczba oddziałów
  • 3 - Liczba zwrotów
Powiązane problemy