Zyklomatische Komplexität beim mehrfachen Aufruf derselben Methode

12

Dank einer Frage bei Code Review geriet ich in eine kleine Meinungsverschiedenheit (was im Wesentlichen eine Gelegenheit ist, etwas zu lernen) darüber, was genau die zyklomatische Komplexität für den folgenden Code ist.

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

Wenn Sie diesen Code in Eclipse schreiben und das Eclipse-Metrik-Plugin verwenden , wird mir mitgeteilt, dass die McCabe Cyclomatic Complexity für die Hauptmethode 2 und für die dort angegebene throMethode 2 ist.

Jemand anderes sagt mir jedoch, dass die Komplexität des thromehrfachen Aufrufs gleich ist number of calls * method complexity, und behauptet daher, dass die Komplexität der Hauptmethode 7 * 2 = 14 ist.

Messen wir verschiedene Dinge? Können wir beide richtig liegen? Oder wie ist die tatsächliche zyklomatische Komplexität hier?

Simon Forsberg
quelle
5
Der CC der Funktion ist zwei, da nur zwei Pfade durchlaufen werden. Der CC des Programms ist höher. Dies ist ein kompletter Stich in die Dunkelheit, aber ich gehe davon aus, dass die Code-Analyse-Software jede Funktion als separate Blackbox nutzt, da es nicht möglich ist, den CC einer gesamten komplexen Anwendung auf einmal zu berechnen.
Phoshi
@Phoshi Wenn Sie das als Antwort schreiben und (wenn möglich) Links bereitstellen würden, die zeigen, dass es eine Trennung der beiden gibt, würde ich diese Antwort gerne akzeptieren.
Simon Forsberg
Wenn Sie alle Pfade von möglichen Ausnahmen in CC - Messungen verursacht zählen, Gott Hilfe der Typ, der auf Refactoring einige triviale Code die Frage gestellt , um die Zahl unter 10 Jahren zu erhalten
mattnz

Antworten:

9

Wenn ich das richtig verstanden habe, ist die zyklomatische Komplexität von main8 - das ist die Anzahl der linear unabhängigen Pfade durch den Code. Sie erhalten entweder eine Ausnahme in einer der sieben Zeilen oder keine, jedoch nie mehr als eine. Jeder dieser möglichen "Ausnahmepunkte" entspricht genau einem anderen Pfad durch den Code.

Ich denke, als McCabe diese Metrik erfand, hatte er keine Programmiersprachen mit Ausnahme der Behandlung im Auge.

Doc Brown
quelle
Aber ist es wirklich wichtig, welche der Zeilen die Ausnahme auslöst?
Simon Forsberg
5
@ SimonAndréForsberg: ja, das tut es. Stellen Sie sich "thro" als Nebeneffekt vor, bei dem ein globaler Zähler beim Aufrufen inkrementiert wird (dies würde die möglichen Pfade durch den Code nicht ändern). Die möglichen Ergebnisse dieses Zählers sind dann 0 bis 7, was beweist, dass der CC mindestens 8 ist.
Doc Brown,
Würden Sie sagen, dass das von mir verwendete Metrik-Plugin einen falschen Wert für die mainMethode ausgibt?
Simon Forsberg
@ SimonAndréForsberg: Nun, ich kenne Ihr Metrik-Plugin nicht, aber 2 ist offensichtlich nicht 8.
Doc Brown
Es gibt einen Link zum Metrik-Plugin in meiner Frage ...
Simon Forsberg
6

Da ich der "andere Typ" bin, antworte ich hier und sage genau, was ich sage (was ich bei anderen Formeln nicht besonders genau gemacht habe).

Anhand des obigen Codebeispiels berechne ich die zyklomatische Komplexität mit 8, und ich habe Kommentare im Code, um zu zeigen, wie ich das berechne. Um die Pfade beschreibe ich eine erfolgreiche Schleife durch betrachten alle die thro()Anrufe als ‚Haupt‘ ‚Codepfad‘ (oder ‚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) {
  }
}

Also zähle ich 8 Codepfade in dieser Hauptmethode, die für mich eine zyklomatische Komplexität von 8 ist.

In Java ausgedrückt, zählt jeder Mechanismus zum Verlassen einer Funktion zu ihrer Komplexität. Eine Methode, die einen Erfolgsstatus aufweist und beispielsweise bis zu 3 Ausnahmen auslöst, verfügt über 4 dokumentierte Austrittspfade.

Die Komplexität einer Methode, die eine solche Funktion aufruft, ist:

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

Ich denke, andere Dinge, die zu berücksichtigen sind, sind, dass die catchKlausel meiner Meinung nach nicht zur Komplexität der Methode beiträgt, sonderncatch einfach das Ziel einer throwsVerzweigung und somit ein Fangblock ist, der das Ziel von Mehrfachzählungen throw1 Mal ist für jeden throwund nicht nur einmal für alles.

rolfl
quelle
Zählen Sie auch die möglichen Zweige für OutOfMemoryExceptions? Ich meine, pedantisch können sie Code-Verzweigungen verursachen, aber niemand zählt sie, da sie den Nutzen der Metrik verwässern.
Telastyn
Nein, ich bin nicht ... und Sie haben Recht, aber im Kontext dieses Arguments zähle ich nur die Ausnahmen, die die Methode auslösen soll. Wenn eine Methode drei Ausnahmen deklariert, der Callinch-Code jedoch eine, spielt es vermutlich catch (Throwable t) {...keine Rolle, wie viele Ausnahmen ausgelöst werden sollen .
Rolfl