Wie kann ich Einfachheit im Code definieren und messen?

13

In meiner vorherigen Frage zur Einfachheit in Bezug auf die Lesbarkeit gab es viele Antworten , die mir geholfen haben, zu erkennen, dass meine Definition und mein Verständnis der Einfachheit im Code möglicherweise falsch waren.

Wie kann ich Einfachheit im Code definieren? Welche Softwaremessungen und -metriken stehen zur Verfügung, um die Code-Einfachheit zu messen?

Richard
quelle
2
@MarkTrapp Es gibt andere Möglichkeiten, die Code-Einfachheit ohne Themen aus dem empirischen Software-Engineering zu diskutieren, Themen, mit denen ich weit weniger vertraut bin. Zum Beispiel die Erörterung der Einfachheit in Bezug auf die Fähigkeit, automatisierte Tests zu schreiben. Meine Fähigkeiten und Kenntnisse ermöglichen es mir, diese Frage aus der Perspektive eines emprischen Software-Ingenieurs zu beantworten, während andere aus alternativen Perspektiven antworten können. Durch Hinzufügen dieser Aussage zu der Frage wird die Anzahl der nützlichen Antworten erheblich begrenzt, sodass sie (IMO) zu lokalisiert ist. Wenn Sie es hinzufügen möchten, können Sie, aber das ist eine gute Frage, wie es ist.
Thomas Owens
2
@ThomasOwens Echte Fragen haben Antworten , keine Ideen oder Meinungen. Es geht bei Stack Exchange genau darum, den Bereich einzugrenzen, damit jeder interpretiert, wie die Frage auf die gleiche Weise beantwortet wird. Es kann mehr als einen Ansatz zur Lösung des Problems geben, aber es gibt nur ein eindeutiges Problem.
In der jetzigen Form gibt es nur sehr wenige Antworten auf diese Frage (meine Antwort befasst sich mit dem Standpunkt der empirischen Softwareentwicklung und den gemeinsamen Metriken - wahrscheinlich gibt es auch andere). Es ist nicht sinnvoll, Antworten mit gültigen Alternativen aus anderen Perspektiven auszuschließen, wie es der Wortlaut dieser Frage tut. Ich bin mit diesen Änderungen nicht einverstanden und die Frage sollte in ihre ursprüngliche Form zurückgesetzt werden.
Thomas Owens
@MarkTrapp Das Problem ist eindeutig: Wie bestimme ich die Code-Einfachheit? Es gibt mehrere gute Antworten. Meins verwendet empirische Software-Engineering-Techniken, um die Komplexität zu messen. Eine andere Möglichkeit ist das Schreiben automatisierter Tests. Wenn es schwierig ist, gute Tests zu schreiben, ist der Code komplex - eine absolut gültige Antwort. Es könnte andere geben, die mir nicht bewusst sind. Wenn Sie die Komplexität / Einfachheit einer Codebasis messen müssen, sollte die Frage so formuliert sein, dass alle Alternativen dargestellt werden können, damit der Fragesteller die beste Lösung für seinen speziellen Fall auswählen kann.
Thomas Owens

Antworten:

16

Die gebräuchlichsten Metriken zum Messen der Komplexität (oder Einfachheit, wenn Sie davon ausgehen, dass Einfachheit das Gegenteil von Komplexität ist) sind McCabes Cyclomatic Complexity und die Halstead Complexity Metrics .

Die zyklomatische Komplexität misst die Anzahl unterschiedlicher Pfade durch eine bestimmte Einheit, normalerweise eine Methode oder Funktion, obwohl sie auch für eine Klasse berechnet werden kann. Mit zunehmender Anzahl von Pfaden wird es schwieriger, sich an den Datenfluss durch ein bestimmtes Modul zu erinnern, der mit dem Konzept des Arbeitsspeichers zusammenhängt . Eine hohe zyklomatische Komplexität deutet auf Schwierigkeiten beim Testen eines Moduls hin. Es sind mehr Testfälle erforderlich, um die verschiedenen Pfade durch das System abzudecken. Es gab auch Studien, die eine hohe zyklomatische Komplexität mit hohen Fehlerraten in Verbindung brachten. In der Regel bedeutet eine zyklomatische Komplexität von 10, dass eine Einheit überprüft und möglicherweise überarbeitet werden sollte.

Bei den Halstead-Komplexitätsmessungen werden die Eingaben von gesamten und verschiedenen Operatoren und Operanden verwendet, um das Volumen, den Schwierigkeitsgrad und den Aufwand eines Codeteils zu berechnen. Die Schwierigkeit (Anzahl der eindeutigen Operatoren / 2) * (Gesamtanzahl der Operanden / Anzahl der eindeutigen Operanden) hängt mit der Fähigkeit zusammen, den Code für Aufgaben wie das Erlernen des Systems oder das Durchführen einer Codeüberprüfung zu lesen und zu verstehen. Sie können dies wiederum auf Systemebene, Klassenebene oder Methoden- / Funktionsebene zählen. Es gibt hier und hier einige Veröffentlichungen zur Berechnung dieser Messungen .

Durch einfaches Zählen von Codezeilen erhalten Sie auch eine Vorstellung von der Komplexität. Mehr Codezeilen bedeuten, dass in einem Modul mehr zu lesen und zu verstehen ist. Ich würde zögern, dies als eigenständige Messung zu verwenden. Stattdessen würde ich es mit anderen Messungen verwenden, z. B. der Anzahl der Fehler in einem bestimmten Modul, um die Fehlerdichte zu erhalten. Eine hohe Fehlerdichte kann auf Probleme beim Schreiben von Tests und beim Durchführen von Codeüberprüfungen hinweisen, die möglicherweise durch komplexen Code verursacht werden.

Fan-In und Fan-Out sind zwei weitere Metriken, die sich auf den Datenfluss beziehen. Wie hier definiert , ist Fan-In die Summe der aufgerufenen Prozeduren, der gelesenen Parameter und der ausgelesenen globalen Variablen. Fan-Out ist die Summe der Prozeduren, die eine bestimmte Prozedur aufrufen. und globale Variablen geschrieben. Auch hier kann ein hohes Ein- und Ausblenden der Lüfter ein Hinweis darauf sein, dass das Modul möglicherweise schwer zu verstehen ist.

In bestimmten Paradigmen gibt es möglicherweise andere Kennzahlen oder Metriken, die ebenfalls nützlich sind. In der objektorientierten Welt können beispielsweise die Überwachung der Kopplung (Wunsch niedrig), der Kohäsion (Wunsch hoch) und der Vererbungstiefe (Wunsch niedrig) verwendet werden, um zu beurteilen, wie einfach oder kompliziert ein System ist.

Natürlich ist es wichtig zu wissen, dass viele Messgrößen und Metriken lediglich Indikatoren sind. Sie müssen Ihr Urteilsvermögen einsetzen, um festzustellen, ob eine Umgestaltung erforderlich ist, um die Einfachheit zu erhöhen, oder ob sich die Mühe nicht lohnt, dies zu tun. Sie können die Messungen vornehmen, die Metriken berechnen und mehr über Ihren Code erfahren, möchten Ihr System jedoch nicht anhand der Zahlen entwerfen. Letztendlich mach was Sinn macht.

Thomas Owens
quelle
5
Ich weiß, dass Sie es erwähnt haben, aber es ist wichtig zu betonen, dass die zyklomatische Komplexität wirklich nur auf Funktions- / Methodenebene nützlich ist und auf höheren Ebenen deutlich subjektiver / nutzloser wird.
Ryathal
Das Problem ist, dass diese Maßnahmen zwar ein allgemeiner Leitfaden sind. Es gibt mehrere Fälle, in denen "schlechte" Programme gut abschneiden, zum Beispiel ein Dutzend Funktionen, bei denen eine einzige Funktion mit zwei weiteren Parametern ausreichen würde, und umgekehrt können viele "gute" Programme, die gut geschriebene Lösungen für ein komplexes Problem sind, abschneiden schlecht.
James Anderson
@ James Ich habe ausdrücklich darauf hingewiesen. Jede Messung oder Metrik muss im Kontext als Indikator dafür gesehen werden, dass etwas betrachtet werden sollte. Es ist das Urteil eines Ingenieurs erforderlich, um festzustellen, ob und welche Korrekturmaßnahmen erforderlich sind. Es gibt jedoch keine empirische Methode, um mögliche Probleme zu ermitteln, es sei denn, Sie erfassen aktiv Daten.
Thomas Owens
7

Anstatt einen formalen Modus zur Definition von Einfachheit zu betrachten, möchte ich Einfachheit eher als ein Attribut der Qualität des Code-Schreibens definieren.

Ich lege kein Maß an Einfachheit an, aber wann nennst du etwas Einfaches oder nicht?

1. Code Traversal:
Wie einfach ist es, durch den Code zu navigieren? Ist es leicht zu erkennen, wo die API-Funktionen geschrieben sind? Ist es einfach, Anrufabläufe zu verstehen, z. B. welche Methoden andere aufrufen (und warum) - sind gute Zustandsautomaten implementiert oder Algorithmen sauber identifiziert?

Wenn die Codeüberquerung einfach ist, ist der Code einfach zu befolgen.

2. Benennung
Während andere Codierungsstandards dazu beitragen, dass der Code übersichtlicher aussieht, ist die Benennung von Klassen / Objektinstanzen / Variablen / Methoden das Wichtigste. Die Verwendung klarer und eindeutiger Namen hat eindeutig einen großen Einfluss auf die Einfachheit des Codes. Wenn es schwierig ist, einen einfachen Namen zu identifizieren, ist dies ein Zeichen dafür, dass Sie die Idee dieser Variablen / Methode überdenken möchten.

3. Interpretation und Referenzen
Hat jede Ihrer Methoden eine klare Rolle zu spielen. Sind die einzelnen Variablen / Attribute leicht zu bestimmen, welche Rolle sie spielen? Wenn ein Teil des Codes etwas tut, das Annahmen impliziert oder sich auf nicht verwandte Variablen auswirkt, kann dies zu einem Alptraum für die Wartung werden.

4. Abhängigkeit oder Kopplung
Dies ist nur anhand des Codes schwer zu beurteilen, wird jedoch sehr deutlich, wenn jemand versucht, Ihre Fehler zu beheben. Wenn sich einige andere Dinge in einem anderen Objekt ändern, ändert sich die Operation hier? Sind diese Änderungen offensichtlich? Müssen Sie die API so oft ändern, um Inhalte aufzunehmen? Dies legt nahe, dass Intermodulbeziehungen nicht einfach sind

5. Benutzereingaben oder Anwendungen
Wie einfach sind die Benutzereingaben oder Anwendungen, die auf der API / UI akzeptiert werden? Wenn Sie mehrere mögliche Benutzer / Anwendungen (für verschiedene Zwecke) angeben müssen - liegen sie auf der Hand? Gibt es Zustände / Details, die sich nicht auf die höhere Abstraktion beziehen, aber dennoch die Schnittstelle hin und her bewegen?

Eine einfache Frage, die ich im Allgemeinen stellen würde, lautet wie folgt: Wenn ich anstelle eines Programms die gleiche von einem Menschen auszuführende Funktion gewünscht hätte, hätte ich diese Informationen auf einem Papierformular ausgefüllt ? Wenn nicht, bin ich hier nicht einfach genug.

Ich werde nicht sagen, dass diese Liste vollständig ist, aber ich denke, Kriterien sind, wie einfach oder schwierig es ist, die Software zu verwenden und zu modifizieren. Das ist einfach

Dipan Mehta
quelle
1
Er bat um Messungen und Metriken. Diese sind subjektiv, daher glaube ich nicht, dass sie sehr zutreffend sind.
bA
@psr Ich stimme dir zu. Das geht auch aus der Antwort von Thomas hervor. Er erwähnte jedoch die Einfachheit der Lesbarkeit . Die Antwort von Thomas befasst sich mit der zyklomatischen Komplexität - hier erfahren Sie, wie komplex es ist, den Code zu testen , und nicht, wie komplex der Code in Bezug auf die Lesbarkeit und möglicherweise die Wartbarkeit ist . Dies sind zwei sehr unterschiedliche Konzepte. Genau deshalb habe ich diese Antwort geschrieben, um den Widerspruch auszudrücken. Leider gibt es meines Wissens keine Metriken, die sich auf die Einfachheit des Codes in Bezug auf die Lesbarkeit beziehen .
Dipan Mehta
"Verwenden Sie Namen, die nicht missverstanden werden können" - IMHO ist das Ziel viel zu hoch, um ein unrealistisches und unmögliches Ziel zu erreichen. Ich würde lieber nicht versuchen, so eindeutig zu sein und einfach "klare und eindeutige Namen verwenden" zu sagen.
Péter Török
@ PéterTörök da stimme ich zu. Ich denke, dass in vielen Organisationen normalerweise sehr klar definierte Regeln für Namenskonventionen bestehen und immer noch einige Unklarheiten über die Intensität der jeweiligen Variablen bestehen. Daher sollte betont werden, dass die Klarheit des Zwecks der Einfachheit im Gegensatz zu einer formalen Regel gleichkommt. Vielleicht bin ich über Bord gegangen, wie ich es beschrieben habe. Vielen Dank.
Dipan Mehta
Die Komplexität von @Dipan Cyclomatic hängt mit der Lesbarkeit des Codes durch den Arbeitsspeicher zusammen. Code mit hoher zyklomatischer Komplexität (oder sogar nur hoher Blocktiefe) ist schwer im Arbeitsspeicher zu halten und daher schwieriger durchzulesen.
Thomas Owens
0

Mir sind keine guten vorhandenen Metriken zur Vereinfachung des Codes bekannt (das bedeutet nicht, dass sie nicht existieren - nur, dass ich nichts über sie weiß). Ich könnte einige vorschlagen, vielleicht werden einige helfen:

  • Einfachheit der verwendeten Sprachfunktionen: Wenn die Sprache Funktionen enthält, die als "erweitert" und "einfach" eingestuft werden können, können Sie die Häufigkeit der erweiterten Funktionen zählen. Wie Sie "fortgeschritten" definieren, ist möglicherweise etwas subjektiver. Ich nehme an, einige könnten sagen, dass dies auch der Messung der "Klugheit" eines Programms gleicht. Ein häufiges Beispiel: Einige könnten sagen, dass der ?:Operator eine "erweiterte" Funktion sein sollte, andere könnten anderer Meinung sein. Ich weiß nicht, wie einfach es wäre, ein Tool zu schreiben, das dies testen kann.

  • Einfachheit der Konstrukte innerhalb des Programms: Sie können die Anzahl der Parameter messen, die eine Funktion akzeptieren wird. Wenn Sie> n % aller Funktionen mit> m Parametern haben , können Sie festlegen, dass dies nicht einfach ist, je nachdem, wie Sie n und m definieren (möglicherweise n = 3 und m = 6?). Ich denke, es gibt einige statische Analysewerkzeuge, die dies messen können - ich denke, JTest hat einfach Funktionen mit> m Parametern gemessen .

  • Sie könnten versuchen, die Anzahl der verschachtelten Schleifen oder Kontrollstrukturen zu zählen. Ich denke, das ist eigentlich keine schlechte Metrik und ich denke, es gibt einen Namen dafür (ich kann mich nicht von oben erinnern). Wieder denke ich, dass es Werkzeuge gibt (wie JTest), die dies bis zu einem gewissen Grad messen können.

  • Sie könnten versuchen, "Refactorability" zu messen. Wenn Ihr Code viele Codeteile enthält, die überarbeitet werden könnten , aber nicht , könnte dies möglicherweise nicht einfach sein. Ich erinnere mich auch an die Zeit, als ich mit JTest zusammengearbeitet habe, dass es auch versucht hat, dies zu messen, aber ich erinnere mich, dass ich in diesem Fall nicht oft damit einverstanden war, also YMMV.

  • Sie könnten versuchen, die Anzahl der Schichten zwischen verschiedenen Teilen Ihres Systems zu messen. Beispiel: Wie viele verschiedene Codeteile berühren Daten, die aus einem Webformular stammen, bevor sie in der Datenbank gespeichert werden? Dies könnte schwierig zu messen sein ...

FrustratedWithFormsDesigner
quelle
2
Ich glaube, # 3 heißt Blocktiefe. Es hängt auch mit der zyklomatischen Komplexität zusammen, wenn entscheidende Kontrollstrukturen beteiligt sind.
Thomas Owens
Keine Downvote-Erklärung?
FrustratedWithFormsDesigner
Kann mit "Einfachheit der Sprachfunktionen" nicht einverstanden sein. Erweiterte Funktionen vereinfachen den Code. Wenn Sie nur die einfachen Grundfunktionen verwenden, wird die eigentliche Funktionsweise des Codes verdeckt. Dies führt unvermeidlich zu undichten Abstraktionsebenen. Erweiterte Sprachfunktionen ermöglichen das Ausdrücken höherer Abstraktionsebenen, wodurch Ihr Code viel dichter und lesbarer wird. Je weiter fortgeschrittene Funktionen Sie verwenden (natürlich mit Bedacht), desto einfacher ist es. Vergleichen Sie einfach einen Code in Matlab (der in der Tat "fortgeschritten" ist) mit einem ähnlichen Fortran-Code, der nur aus grundlegenden Funktionen besteht.
SK-logic
Und ich bin auch nicht mit einer Anzahl von Layern einverstanden. Wenn Sie etwas in einem Dutzend einfacher und sauberer Schritte oder in einer verdrehten Transformation tun können, tun Sie es besser in mehreren Schritten. Viele einfache und klar getrennte Schichten sind viel besser (und einfacher) als eine einzelne verdrillte Schicht.
SK-logic
@ SK-logic: Ich denke, ich hätte das eine "Cleverness" nennen sollen, es ist näher an dem, was ich meinte. Ich würde nur sagen, dass Dinge wie ?:ein Problem sind, wenn sie 5 tief verschachtelt sind. Sauber getrennte Schichten sind besser als eine gewundene Schicht. Aber 7 meist redundante Schichten, bei denen nur 2 oder 3 notwendig waren, ist eine schlechte Sache.
FrustratedWithFormsDesigner