Cyklomatyczna złożoność przy wielokrotnym wywoływaniu tej samej metody


12

Dzięki pytaniu na Code Review doszedłem do małego nieporozumienia (co w zasadzie jest okazją do nauczenia się czegoś) na temat tego, czym dokładnie jest złożoność cykliczna dla poniższego kodu.

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

Pisząc ten kod w Eclipse i używając wtyczki Eclipse , mówi mi, że złożoność cykliczna McCabe dla głównej metody wynosi 2, a dla thrometody 2.

Jednak ktoś inny mówi mi, że złożoność wywoływania throwielokrotnego jest number of calls * method complexity, i dlatego twierdzi, że złożoność głównej metody wynosi 7 * 2 = 14.

Czy mierzymy różne rzeczy? Czy oboje możemy mieć rację? A jaka jest tutaj rzeczywista złożoność cyklomatyczna?


5
CC funkcji wynosi dwa, ponieważ istnieją tylko dwie ścieżki. CC programu jest wyższy. Jest to kompletna próba w ciemności, ale zakładam, że oprogramowanie do analizy kodu przyjmuje każdą funkcję jako osobną czarną skrzynkę ze względu na niemożność obliczenia CC całej złożonej aplikacji za jednym razem.
— Phoshi

@ Phoshi Jeśli napiszesz to jako odpowiedź i (jeśli to możliwe) podasz linki, które pokazują, że istnieje ich rozdzielenie, chętnie zaakceptuję tę odpowiedź.
— Simon Forsberg

Jeśli policzysz wszystkie ścieżki spowodowane możliwymi wyjątkami w pomiarach CC, niech Bóg pomoże facetowi, który zadał pytanie o refaktoryzacji jakiegoś trywialnego kodu, aby uzyskać liczbę poniżej 10.
— Mattnz

Odpowiedzi:


9

Kiedy rozumiał prawidłowo, cykliczna Złożoność z mainwynosi 8 - to liczba liniowo niezależne ścieżki przez kod. Albo dostajesz wyjątek w jednym z siedmiu wierszy, albo żaden, ale nigdy więcej niż jeden. Każdy z tych możliwych „punktów wyjątku” odpowiada dokładnie jednej innej ścieżce w kodzie.

Myślę, że kiedy McCabe wynalazł tę metrykę, nie miał języków programowania z myślą o obsłudze wyjątków.


Ale czy naprawdę ma znaczenie, która z linii generuje wyjątek?
— Simon Forsberg

5
@ SimonAndréForsberg: tak, robi. Pomyśl o tym, że „thro” ma efekt uboczny, w którym zwiększa licznik globalny, gdy jest wywoływany (nie zmieniałoby to możliwych ścieżek w kodzie). Możliwe wyniki tego licznika wynoszą więc od 0 do 7, więc dowodzi to, że CC wynosi co najmniej 8.
— Doc Brown

Czy powiedziałbyś, że wtyczka metryk, której używam, zgłasza niepoprawną wartość mainmetody?
— Simon Forsberg

@ SimonAndréForsberg: cóż, nie znam twojej wtyczki metryk, ale 2 oczywiście nie jest 8.
— Doc Brown

W moim pytaniu jest link do wtyczki metryk ...
— Simon Forsberg

6

Będąc „drugim facetem”, odpowiem tutaj i sprecyzuję to, co mówię (co nie było szczególnie precyzyjne w stosunku do innych formuł).

Korzystając z powyższego przykładu kodu, obliczam złożoność cykliczną jako 8 i mam w kodzie komentarze, aby pokazać, jak to obliczam. Aby opisać ścieżki Rozważę udanej pętlę przez wszystkich tych thro()połączeń jak „” „głównej ścieżki kodu” (lub „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) {
  }
}

Tak więc liczę 8 ścieżek kodu w tej głównej metodzie, która według mnie jest złożonością cykliczną wynoszącą 8.

Mówiąc językiem Java, każdy mechanizm wyjścia z funkcji liczy się do jej złożoności, więc metoda, która ma stan powodzenia, i rzuca, na przykład, być może do 3 wyjątków, ma 4 udokumentowane ścieżki wyjścia.

Złożoność metody wywołującej taką funkcję to:

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

Sądzę, że należy wziąć pod uwagę inne kwestie: moim zdaniem catchklauzula nie przyczynia się do złożoności metody, catchjest po prostu celem throwsgałęzi, a zatem blokiem wychwytywania, który jest celem wielu throws, zlicza 1 raz dla każdego throw, i nie tylko raz na wszystko.


Czy liczysz również możliwe gałęzie wyjątków OutOfMemory? Mam na myśli pedantycznie, że mogą powodować rozgałęzienia kodu, ale nikt ich nie liczy, ponieważ osłabiają użyteczność metryki.
— Telastyn

Nie, nie jestem ... i masz rację, ale w kontekście tego argumentu liczę tylko wyjątki, które metoda ma zgłosić. Ponadto, jeśli metoda deklaruje trzy wyjątki, ale kod wywołania robi a, catch (Throwable t) {...to myślę, że nie ma znaczenia, ile wyjątków deklaruje zgłosić .
— rolfl
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.