Ich habe in letzter Zeit einen Code überarbeitet und dachte, ich hätte gute Arbeit geleistet. Ich ließ 980 Codezeilen auf 450 fallen und halbierte die Anzahl der Klassen.
Als ich dies meinen Kollegen zeigte, waren sich einige nicht einig, dass dies eine Verbesserung war.
Sie sagten: "Weniger Codezeilen sind nicht unbedingt besser."
Ich kann sehen, dass es extreme Fälle geben kann, in denen Leute wirklich lange Zeilen schreiben und / oder alles in einer einzigen Methode zusammenfassen, um ein paar Zeilen zu speichern, aber das habe ich nicht getan. Der Code ist meiner Meinung nach gut strukturiert und einfacher zu verstehen / zu pflegen, da er nur halb so groß ist.
Ich kämpfe darum, zu verstehen, warum jeder mit doppelt so viel Code arbeiten möchte, wie für die Erledigung eines Auftrags erforderlich ist, und frage mich, ob sich jemand genauso fühlt wie meine Kollegen und gute Argumente dafür finden kann, mehr Code statt weniger zu haben ?
quelle
Antworten:
Eine dünne Person ist nicht unbedingt gesünder als eine übergewichtige Person.
Eine 980 Zeilen lange Kindergeschichte ist leichter zu lesen als eine 450 Zeilen lange Physikarbeit.
Es gibt viele Attribute, die die Qualität Ihres Codes bestimmen. Einige werden einfach berechnet, wie zyklomatische Komplexität und Halstead-Komplexität . Andere sind klarer definiert, z. B. Zusammenhalt , Lesbarkeit, Verständlichkeit, Erweiterbarkeit, Robustheit, Korrektheit, Selbstdokumentation, Sauberkeit, Testbarkeit und vieles mehr.
Es könnte zum Beispiel sein, dass Sie, während Sie die Gesamtlänge des Codes reduzierten, zusätzliche ungerechtfertigte Komplexität eingeführt und den Code kryptischer gemacht haben.
Das Aufteilen eines langen Codeteils in winzige Methoden kann sowohl schädlich als auch vorteilhaft sein .
Bitten Sie Ihre Kollegen, Ihnen spezifisches Feedback zu geben, warum sie glauben, dass Ihre Umgestaltungsbemühungen zu einem unerwünschten Ergebnis geführt haben.
quelle
Interessanterweise arbeiten ein Kollege und ich gerade an einem Refactor, der die Anzahl der Klassen und Funktionen um etwas weniger als das Doppelte erhöhen wird , obwohl die Codezeilen in etwa gleich bleiben. Also habe ich zufällig ein gutes Beispiel.
In unserem Fall hatten wir eine Abstraktionsebene, die eigentlich zwei hätte sein sollen. Alles war in die UI-Schicht gepfercht. Durch die Aufteilung in zwei Schichten wird alles zusammenhängender und das Testen und Warten der einzelnen Teile wird viel einfacher.
Es ist nicht die Größe des Codes, die Ihre Kollegen stört, es ist etwas anderes. Wenn sie es nicht artikulieren können, versuchen Sie, den Code selbst so zu betrachten, als hätten Sie die alte Implementierung noch nie gesehen, und bewerten Sie ihn nicht nur im Vergleich, sondern nach seinen eigenen Vorzügen. Manchmal verliere ich bei langen Überarbeitungen das ursprüngliche Ziel aus den Augen und gehe zu weit. Nehmen Sie einen kritischen "Gesamtüberblick" und bringen Sie ihn wieder auf den richtigen Weg, vielleicht mit Hilfe eines Programmierers, dessen Rat Sie schätzen.
quelle
Ein Zitat, das oft Albert Einstein zugeschrieben wird, fällt mir ein:
Wenn Sie beim Kürzen über Bord gehen, kann dies das Lesen des Codes erschweren. Da "leicht / schwer zu lesen" ein sehr subjektiver Begriff sein kann, erkläre ich genau, was ich damit meine: ein Maß für den Schwierigkeitsgrad, den ein erfahrener Entwickler bei der Bestimmung "Was macht dieser Code?" durch bloßes Betrachten der Quelle ohne die Hilfe spezialisierter Werkzeuge.
Sprachen wie Java und Pascal sind berüchtigt für ihre Ausführlichkeit. Die Leute verweisen oft auf bestimmte syntaktische Elemente und sagen spöttisch: "Sie sind nur da, um die Arbeit des Compilers zu erleichtern." Dies ist mehr oder weniger wahr, mit Ausnahme des "gerechten" Teils. Je expliziter die Informationen sind, desto einfacher ist es, den Code zu lesen und zu verstehen, und zwar nicht nur für einen Compiler, sondern auch für einen Menschen.
Wenn ich sage
var x = 2 + 2;
, ist es sofort offensichtlich, dassx
es sich um eine ganze Zahl handeln soll. Aber wenn ich sagevar foo = value.Response;
, ist es viel weniger klar, wasfoo
repräsentiert oder welche Eigenschaften und Fähigkeiten es hat. Auch wenn der Compiler leicht darauf schließen kann, ist die kognitive Anstrengung für eine Person sehr viel größer.Denken Sie daran, dass Programme geschrieben werden müssen, damit Benutzer sie lesen können, und nur im Übrigen, damit Maschinen ausgeführt werden können. (Ironischerweise stammt dieses Zitat aus einem Lehrbuch, das einer Sprache gewidmet ist, die bekanntermaßen extrem schwer zu lesen ist.) Es ist eine gute Idee, überflüssige Dinge zu entfernen, aber keinen Code zu entfernen, der es Ihren Mitmenschen leichter macht, sie zu lesen Finde heraus, was los ist, auch wenn es für das Programm, das geschrieben wird, nicht unbedingt erforderlich ist.
quelle
var
Beispiel eignet sich nicht besonders gut zur Vereinfachung, da das Lesen und Verstehen des Codes in der Regel das Herausfinden von Verhaltensweisen auf einer bestimmten Abstraktionsebene erfordert hilft Ihnen, niedrigere Abstraktionen zu verstehen). Ein besseres Beispiel wären mehrere Zeilen einfachen Codes, die zu einer einzigenif ((x = Foo()) != (y = Bar()) && CheckResult(x, y))
verschachtelten Anweisung zusammengefasst werden - z. B. dauert es eine Weile, bis man sich zurechtfindet und die Typen kennt, die im Geringsten helfenx
odery
nicht helfen.Längere Codes können möglicherweise besser lesbar sein. Normalerweise ist das Gegenteil der Fall, aber es gibt viele Ausnahmen - von denen einige in anderen Antworten aufgeführt sind.
Aber schauen wir mal aus einem anderen Blickwinkel. Wir gehen davon aus, dass der neue Code von den meisten erfahrenen Programmierern als überlegen angesehen wird, die die beiden Codeteile sehen, ohne zusätzliche Kenntnisse der Unternehmenskultur, der Codebasis oder der Roadmap zu haben. Auch dann gibt es viele Gründe, den neuen Code abzulehnen. Der Kürze halber werde ich "Leute, die den neuen Code kritisieren " nennen .
quelle
Welche Art von Code besser ist, hängt möglicherweise vom Fachwissen der Programmierer und auch von den von ihnen verwendeten Tools ab. Hier ist zum Beispiel der Grund, warum was normalerweise als schlecht geschriebener Code betrachtet wird, in manchen Situationen effektiver sein kann als gut geschriebener objektorientierter Code, der die Vererbung voll ausnutzt:
(1) Einige Programmierer haben einfach kein intuitives Verständnis der objektorientierten Programmierung. Wenn Ihre Metapher für ein Softwareprojekt ein Stromkreis ist, werden Sie eine Menge Code-Duplizierung erwarten. Sie werden mehr oder weniger die gleichen Methoden in vielen Klassen sehen wollen. Sie werden sich wie zu Hause fühlen. Und ein Projekt, in dem Sie Methoden in Elternklassen oder sogar in Großelternklassen nachschlagen müssen, um zu sehen, was vor sich geht, fühlt sich möglicherweise feindlich an. Sie möchten nicht verstehen, wie die übergeordnete Klasse funktioniert, und dann verstehen, wie sich die aktuelle Klasse unterscheidet. Sie möchten direkt verstehen, wie die aktuelle Klasse funktioniert, und Sie finden die Tatsache, dass die Informationen über mehrere Dateien verteilt sind, verwirrend.
Wenn Sie nur ein bestimmtes Problem in einer bestimmten Klasse beheben möchten, möchten Sie möglicherweise nicht darüber nachdenken, ob Sie das Problem direkt in der Basisklasse beheben oder die Methode in Ihrer aktuellen Interessenklasse überschreiben möchten. (Ohne Vererbung müssten Sie keine bewusste Entscheidung treffen. Standardmäßig werden ähnliche Probleme in ähnlichen Klassen einfach ignoriert, bis sie als Fehler gemeldet werden.) Dieser letzte Aspekt ist kein wirklich gültiges Argument, obwohl er möglicherweise einige der Probleme erklärt Opposition.
(2) Einige Programmierer verwenden den Debugger häufig. Auch wenn ich im Allgemeinen fest auf der Seite der Code-Vererbung und der Vermeidung von Duplikaten stehe, teile ich einen Teil der Frustration, die ich in (1) beim Debuggen von objektorientiertem Code beschrieben habe. Wenn Sie der Codeausführung folgen, springt sie manchmal zwischen (Vorfahren-) Klassen umher, obwohl sie im selben Objekt verbleibt. Wenn Sie einen Haltepunkt in gut geschriebenem Code setzen, wird dieser mit größerer Wahrscheinlichkeit ausgelöst, wenn er nicht hilfreich ist. Daher müssen Sie möglicherweise Anstrengungen unternehmen, um ihn bedingt (sofern dies praktikabel ist) oder sogar manuell viele Male fortzusetzen, bevor der entsprechende Auslöser ausgeführt wird.
quelle
Es kommt ganz darauf an. Ich habe an einem Projekt gearbeitet, das keine booleschen Variablen als Funktionsparameter zulässt, sondern
enum
für jede Option ein dediziertes benötigt .Damit,
ist viel ausführlicher als
Jedoch,
ist viel lesbarer als
Der Compiler sollte für beide den gleichen Code generieren, damit mit der kürzeren Form nichts erreicht werden kann.
quelle
Ich würde sagen, der Zusammenhalt könnte ein Problem sein.
Beispiel: In einer Webanwendung haben Sie eine Admin-Seite, auf der Sie alle Produkte indizieren. Dies ist im Wesentlichen derselbe Code (Index), den Sie in einer Homepage-Situation verwenden würden, um nur die Produkte zu indizieren.
Wenn Sie sich dazu entschließen, alles zu partitionieren, damit Sie trocken und schlank bleiben, müssen Sie eine Menge Bedingungen hinzufügen, um festzustellen, ob der Benutzer, der surft, ein Administrator ist oder nicht, und den Code mit unnötigen Dingen überfüllen, die ihn beispielsweise sehr unlesbar machen ein Designer!
Selbst wenn der Code in einer solchen Situation ziemlich gleich ist, nur weil er auf etwas anderes skaliert werden könnte und sich die Anwendungsfälle geringfügig ändern könnten, wäre es schlecht, nach jedem von ihnen zu suchen, indem Sie Bedingungen und ifs hinzufügen. Eine gute Strategie wäre es also, das DRY-Konzept aufzugeben und den Code in wartbare Teile zu zerlegen.
quelle
Wenn weniger Code weniger wartbar / erweiterbar ist als mehr Code. Durch das Refactoring auf Code-Prägnanz werden häufig "unnötige" Codekonstrukte im Interesse von LoC entfernt. Das Problem ist, dass diese Codekonstrukte, wie parallele Schnittstellendeklarationen, extrahierte Methoden / Unterklassen usw., erforderlich sind, falls dieser Code jemals mehr tun muss als derzeit oder anders. Im Extremfall funktionieren bestimmte, auf das jeweilige Problem zugeschnittene Lösungen möglicherweise überhaupt nicht, wenn sich die Problemdefinition nur geringfügig ändert.
Ein Beispiel; Sie haben eine Liste von ganzen Zahlen. Jede dieser ganzen Zahlen hat einen doppelten Wert in der Liste, mit einer Ausnahme. Ihr Algorithmus muss diesen ungepaarten Wert finden. Die allgemeine Lösung besteht darin, jede Zahl mit jeder anderen zu vergleichen, bis Sie eine Zahl finden, die keine Dupe in der Liste hat, was eine N ^ 2-malige Operation ist. Sie können auch ein Histogramm mit einer Hash-Tabelle erstellen, dies ist jedoch sehr ineffizient. Sie können es jedoch mit einer bitweisen XOR-Operation zu einer linearen Zeit und zu einem konstanten Raum machen. XOR jede ganze Zahl gegen eine laufende "Summe" (beginnend mit Null), und am Ende wird die laufende Summe der Wert Ihrer ungepaarten ganzen Zahl sein. Sehr elegant. Bis sich die Anforderungen ändern und mehr als eine Zahl in der Liste ungepaart sein könnte oder die ganzen Zahlen Null enthalten. Jetzt gibt Ihr Programm entweder fehlerhafte oder mehrdeutige Ergebnisse zurück (bedeutet dies, wenn es Null zurückgibt, dass alle Elemente gepaart sind oder dass das ungepaarte Element Null ist). Dies ist das Problem "cleverer" Implementierungen in der realen Programmierung.
quelle
Computercode muss eine Reihe von Dingen erledigen. Ein "minimalistischer" Code, der diese Dinge nicht tut, ist kein guter Code.
Beispielsweise sollte ein Computerprogramm alle möglichen Fälle abdecken (oder zumindest alle wahrscheinlichen Fälle). Wenn ein Teil des Codes nur den einen "Basisfall" abdeckt und andere ignoriert, ist er kein guter Code, auch wenn er kurz ist.
Computercode sollte "skalierbar" sein. Ein kryptischer Code funktioniert möglicherweise nur für eine spezielle Anwendung, während ein längeres, aber offeneres Programm das Hinzufügen neuer Anwendungen erleichtert.
Computercode sollte klar sein. Wie ein anderer Antwortender gezeigt hat, ist es für einen Hardcore-Codierer möglich, eine einzeilige "algorithmische" Funktion zu erzeugen, die die Aufgabe erfüllt. Aber der Einzeiler musste in fünf verschiedene "Sätze" aufgeteilt werden, bevor es dem durchschnittlichen Programmierer klar war.
quelle
Rechenleistung. Wenn Sie die Pipeline optimieren oder Teile Ihres Codes parallel ausführen, kann es vorteilhaft sein, z. B. keine Schleife von 1 bis 400, sondern von 1 bis 50 auszuführen und 8 Instanzen ähnlichen Codes in jede Schleife einzufügen. Ich gehe nicht davon aus, dass dies in Ihrer Situation der Fall war, aber es ist ein Beispiel, bei dem mehr Leitungen (in Bezug auf die Leistung) besser sind.
quelle