Complessità ciclomatica quando si chiama lo stesso metodo più volte


12

Grazie a una domanda alla Code Review ho avuto un piccolo disaccordo (che essenzialmente è un'opportunità per imparare qualcosa) su cosa sia esattamente la complessità ciclomatica per il codice qui sotto.

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

Quando scrivo questo codice in Eclipse e si usa il plug-in delle metriche Eclipse , mi dice che la complessità ciclica McCabe per il metodo principale è 2 e per il thrometodo che dice 2.

Tuttavia, qualcun altro mi dice che la complessità della chiamata thropiù volte è number of calls * method complexity, e quindi sostiene che la complessità del metodo principale è 7 * 2 = 14.

Stiamo misurando cose diverse? Entrambi possiamo essere corretti? O qual è l'attuale complessità ciclomatica?


5
Il CC della funzione è due, in quanto vi sono solo due percorsi. Il CC del programma è più alto. Questa è una pugnalata al buio, ma presumo che il software di analisi del codice prenda ogni funzione come una scatola nera separata a causa della fattibilità del calcolo del CC di un'intera complessa applicazione in una volta sola.
Phoshi,

@Phoshi Se lo scrivessi come risposta e (se possibile) fornissi dei collegamenti che mostrano che esiste una separazione dei due, accetterei volentieri quella risposta.
Simon Forsberg,

Se conti tutti i percorsi causati da possibili eccezioni nelle misurazioni CC, dio aiuta il ragazzo che ha posto la domanda sul refactoring di un codice banale per ottenere il numero sotto 10.
mattnz

Risposte:


9

Quando l'ho capito correttamente, la complessità ciclomatica di mainè 8 - questo è il numero di percorsi linearmente indipendenti attraverso il codice. O ottieni un'eccezione su una delle sette righe o nessuna, ma mai più di una. Ciascuno di quei possibili "punti di eccezione" corrisponde esattamente a un percorso diverso attraverso il codice.

Immagino che quando McCabe inventò quella metrica, non aveva linguaggi di programmazione con in mente la gestione delle eccezioni.


Ma importa davvero quale delle linee che genera l'eccezione?
Simon Forsberg,

5
@ SimonAndréForsberg: sì, lo fa. Pensa che "thro" abbia un effetto collaterale in cui incrementa un contatore globale quando viene chiamato (ciò non cambierebbe i possibili percorsi attraverso il codice). I possibili risultati di quel contatore sono quindi da 0 a 7, quindi questo dimostra che il CC è almeno 8.
Doc Brown,

Diresti che il plug-in delle metriche che sto utilizzando segnala un valore errato per il mainmetodo?
Simon Forsberg,

@ SimonAndréForsberg: beh, non conosco il tuo plug-in delle metriche, ma ovviamente 2 non è 8.
Doc Brown,

C'è un link al plug-in delle metriche nella mia domanda ....
Simon Forsberg,

6

Essendo "l'altro ragazzo", risponderò qui, e sarò preciso su ciò che dico (che non ero particolarmente preciso con gli altri moduli).

Utilizzando l'esempio di codice sopra riportato, calcolo la complessità ciclomatica come 8 e ho commenti nel codice per mostrare come lo calcolo. Per descrivere i percorsi considererò un ciclo riuscito attraverso tutte le thro()chiamate come "percorso principale" "(o" 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) {
  }
}

Quindi, conto 8 percorsi di codice in questo metodo principale, che per me è una complessità ciclomatica di 8.

In termini Java, ogni meccanismo per uscire da una funzione conta per la sua complessità, quindi un metodo che ha uno stato di successo e, ad esempio, genera fino a 3 eccezioni, ha 4 percorsi di uscita documentati.

La complessità di un metodo che chiama tale funzione è:

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

Penso che altre cose da considerare, è che, secondo me, la catchclausola non contribuisce alla complessità del metodo, catchè semplicemente il bersaglio di un throwsramo, e quindi un blocco di cattura che è il bersaglio di più throwconteggi 1 volta per ciascuno throw, e non solo una volta per tutto.


Stai contando i possibili rami anche per OutOfMemoryExceptions? Intendo pedanticamente possono causare rami di codice, ma nessuno li conta poiché diluiscono l'utilità della metrica.
Telastyn,

No, non lo sono ... e hai ragione, ma, nel contesto di questo argomento, conto solo le eccezioni che il metodo viene dichiarato. Inoltre, se un metodo dichiara tre eccezioni, ma il codice callinch fa un catch (Throwable t) {...allora immagino che non importa quante eccezioni dichiara di lanciare.
rolfl,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.