Reduzieren iterative Methoden die zyklomatische Komplexität und verbessern sie die Unterstützbarkeit?

11

Reduzieren iterative Methoden, wie sie üblicherweise in modernen Sprachen wie C #, JavaScript und (hoffentlich) in Java 8 zu finden sind, die Auswirkungen der zyklomatischen Komplexität auf die Verständlichkeit und Unterstützbarkeit von Code?

Zum Beispiel könnten wir in C # den folgenden Code haben:

List<String> filteredList = new List<String>();

foreach (String s in originalList){
   if (matches(s)){
      filteredList.add(s);
   }
}

Dies hat eine einfache zyklomatische Komplexität von 2.

Wir könnten dies leicht umschreiben als:

List<String> filteredList = originalList.where(s => matches(s));

Welches hat eine einfache zyklomatische Komplexität von 0.

Führt dies tatsächlich zu mehr unterstützbarem Code? Gibt es aktuelle Forschungsergebnisse zu diesem Thema?

Kreuz
quelle
2
In Ihrem Titel wird nach der Reduzierung der zyklomatischen Komplexität gefragt, in Ihrem Text wird jedoch die CC-Reduzierung und nach der Wartbarkeit gefragt. Dies kann eine wertvolle Frage sein. Können Sie den Titel genauer bearbeiten?
Kilian Foth
@ KilianFoth Ist das besser?
C. Ross
Ich würde iterative Methoden mit iterativen Entwicklungsmetodologien verknüpfen. Ich denke, ein besserer Begriff wären Funktionen höherer Ordnung. (Abgesehen davon haben Methoden keinen Rückgabetyp / void, während Funktionen etwas zurückgeben.)
Johannes Rudolph
@JohannesRudolph "Methoden haben keinen Rückgabetyp / void, während Funktionen etwas zurückgeben" - in welcher Sprache ist das wahr? Ich habe das noch nie gehört. Tatsächlich war der einzige wirkliche Unterschied, den ich jemals gehört habe, dass Methoden einer Klasse zugeordnet sind, Funktionen jedoch nicht.
Dreier

Antworten:

14

Ich vermute, dass Sie gerade die Komplexität geteilt / verschoben haben. Es wurde verringert, weil Sie die Implementierung .where()in Ihrem CC nicht zählen .

Der Gesamt-CC hat sich nicht wirklich verschoben, der CC Ihres eigenen Codes hat sich verringert, nur weil er jetzt in den Code des Frameworks verschoben wurde.

Ich würde sagen, es ist wartbarer. Wenn es ein Merkmal der Sprache ist, verwenden Sie es. Es ist kein "ohh, ich verstehe, es ist ein cleverer Trick" , den Sie verwenden, sondern nur eine einfache Inline-Reduktionsfunktion.

Clement Herreman
quelle
11

Alles, was Sie tun, ist, einen Fehler in der zyklomatischen Komplexität als Metrik hervorzuheben. Die Komplexität des Codes hat sich wirklich nicht geändert. Sie haben im ersten Beispiel einen expliziten Zweig und müssen verstehen, dass im zweiten ein impliziter Zweig vorhanden ist. Die zweite ist klarer und leichter zu verstehen, vorausgesetzt, Sie verstehen die Syntax und da sie weniger grundlegende Syntax verwendet, kann dies ein Problem sein.

Ryathal
quelle
1
Man könnte argumentieren, dass die zyklomatische Komplexität des eigenen Codes hier reduziert wird, da wherehier wahrscheinlich ein Framework vorliegt . Ich denke, dass die zyklomatische Komplexität als Metrik auch hier nützlich ist, da eine Aufteilung einer Funktion in mehrere zwar die Gesamtkomplexität des Systems nicht verringert (sie erhöht sie jedoch häufig, wenn auch nur geringfügig), Sie jedoch besser bestimmen können, wann eine Ein Teil des Systems wird zu komplex und muss abgebaut werden.
Dreier
6

Um die Frage objektiv zu beantworten, benötigen wir eine Art Metrik für die Wartbarkeit. Die zyklomatische Komplexität selbst ist kein Maß für die Wartbarkeit, aber sie ist Bestandteil einiger Metriken, die vorgeben, die Wartbarkeit zu messen. Die Formel für den Wartbarkeitsindex lautet beispielsweise:

MI = 171 - 5.2 * ln(V) - 0.23 * (G) - 16.2 * ln(LOC)

Wo Gist die zyklomatische Komplexität des betreffenden Codes? Daher verbessert das Reduzieren der zyklomatischen Komplexität eines Codeteils per Definition den Wartbarkeitsindex des Codes und andere Metriken, die ebenfalls die zyklomatische Komplexität verwenden .

Es ist schwer zu sagen, ob die Art der Änderung, die Sie vorschlagen, das Programm für Programmierer wartbarer erscheinen lässt . Das hängt wahrscheinlich davon ab, wie vertraut sie (in Ihrem Fall) mit der whereMethode sind.

Caleb
quelle
Warnung: Magische Zahlen! (>_<)
Izkata
Es gibt nur dieses eine kleine Problem mit dem Wartbarkeitsindex. Die drei Komponenten sind Halstead-Volumen, zyklomatische Komplexität und Codezeilen. Es wurde gezeigt, dass sowohl das Halstead-Volumen als auch die zyklomatische Komplexität sehr stark mit den Codezeilen korrelieren. Dies bedeutet, dass eine alternative Näherungsgleichung für den Wartbarkeitsindex abgeleitet werden könnte, die NUR von Codezeilen abhängt, die fast so genau wie das Original wäre und erheblich weniger Rechenschwierigkeiten aufweist.
John R. Strohm
@ JohnR.Strohm Ich denke, Sie würden ein ähnliches Ergebnis erzielen, selbst wenn Sie nur LOC als Wartbarkeitsmetrik verwenden. Die Änderung des OP reduziert den LOC von 4 auf 1, und andere Änderungen, die die zyklomatische Komplexität verringern, verringern in ähnlicher Weise wahrscheinlich auch den LOC. In jedem Fall kommt es wirklich darauf an, wie Sie die Wartbarkeit definieren und messen.
Caleb
1
@Caleb: Einverstanden. Der Punkt, den ich versuche, ist, dass die Leute viel zu oft diese wirklich komplizierten Metriken und Kombinationen von Metriken wählen, ohne zu bemerken, dass fast alle von ihnen gezeigt wurden, dass sie stark mit echtem Code korrelieren, mit einfachen alten Codezeilen (LOC). und haben daher keinen prädiktiveren oder beschreibenderen Wert als LOC.
John R. Strohm
3

Weil gezeigt wurde, dass die zyklomatische Komplexität (CC) sehr stark mit der Codegröße korreliert, "so sehr, dass man sagen kann, dass CC absolut keine eigene Erklärungskraft hat". Was Sie wirklich fragen, ist, ob "iterative Methoden, wie sie üblicherweise in modernen Sprachen wie C #, JavaScript und (hoffentlich) in Java 8 zu finden sind, die Auswirkungen der Codegröße auf die Verständlichkeit und Unterstützbarkeit von Code verringern."

An diesem Punkt würde man hoffen, dass die Antwort offensichtlich wäre. Es ist seit Jahrzehnten bekannt, dass kürzerer Code im Allgemeinen leichter zu verstehen, zu warten und zu unterstützen ist.

John R. Strohm
quelle
2

Wenn Sie über den Rohzustand der zyklomatischen Komplexität sprechen, sicher. Sie haben es nur von 2 auf 0 gebracht. Wenn Sie nach Zahlen gehen, dann nach reinem Gewinn.

Aus praktischer (sprich: menschlicher) Perspektive würde ich jedoch argumentieren, dass Sie die Komplexität tatsächlich um 2 erhöht haben . Ein Punkt davon ist die Tatsache, dass jetzt ein anderer Programmierer Kenntnisse über die fließende Syntax LINQ mitbringen oder erwerben muss, um dies zu verstehen Code.

Ein weiterer Punkt mit erhöhten Schwierigkeiten ergibt sich aus dem Verständnis der Lambda-Ausdrücke. Obwohl ein Lambda in diesem Fall ziemlich einfach ist , müssen einige Paradigmenwechsel vorgenommen werden, um sie vollständig zu würdigen.

In diesem Fall ist die Verwendung a.where(x => matches(x, arg))nicht schrecklich, und ehrlich gesagt ist dies eine großartige Möglichkeit, um Mitarbeiter dazu zu bringen, LINQ- und Lambda-Ausdrücke zum ersten Mal zu sehen und damit zu arbeiten (ich habe einigen ehemaligen Mitarbeitern, die dies verwenden, tatsächlich ein Tutorial zu LINQ / Lambdas vorgestellt und andere Codesätze mit großer Wirkung.) Die Verwendung dieser erfordert jedoch einige Kenntnisse.

Ich empfehle Vorsicht, da ich Fälle gesehen habe, in denen der LINQ-Refaktor tatsächlich wesentlich schlechter zu lesen ist als das, was diese foreachSchleife wird.

Andrew Gray
quelle
1

Subjektiv hängt es vom Entwicklerpublikum ab, ob es Lambda-Ausdrücke versteht.

List<String> filteredList = originalList.where(s => matches(s));

ist schneller zu verstehen und vielleicht etwas einfacher. Ich wäre mehr besorgt über die Verwendung von s und Übereinstimmungen (). Weder ist selbstbeschreibend, so etwas wie;

List<String> filteredList = 
    originalList.where(stringToBeTested => matchesNameTest(stringToBeTested));

Oder

List<String> filteredList = 
        originalList.where(originalListString => matchesNameTest(originalListString));

gibt dem Entwickler aussagekräftigere Informationen und ist einfacher zu analysieren, ohne in die Funktion match () eintauchen zu müssen, um festzustellen, welche Übereinstimmung ausgeführt wird.

Bei der Wartbarkeit geht es nicht nur um die Fähigkeit, den Code zu verstehen, sondern hauptsächlich um die Geschwindigkeit und Genauigkeit, mit der man den Code verstehen kann.

AceCTO
quelle
2
Hallo Ass. Es ist ein Beispiel, das absichtlich keinen semantischen Inhalt enthält, um die Struktur hervorzuheben.
C. Ross