Moderatornotiz Auf
diese Frage wurden bereits siebzehn Antworten gepostet. Bevor Sie eine neue Antwort veröffentlichen, lesen Sie bitte die vorhandenen Antworten und vergewissern Sie sich, dass Ihre Sichtweise nicht bereits ausreichend abgedeckt ist.
Ich habe einige der in Robert Martins Buch "Clean Code" empfohlenen Vorgehensweisen befolgt, insbesondere diejenigen, die für die Art von Software gelten, mit der ich arbeite, und diejenigen, die für mich sinnvoll sind (ich befolge sie nicht als Dogma). .
Ein Nebeneffekt, den ich bemerkt habe, ist, dass der "saubere" Code, den ich schreibe, mehr Code ist, als wenn ich einige Praktiken nicht befolgt hätte. Die spezifischen Praktiken, die dazu führen, sind:
- Bedingungen einkapseln
Also statt
if(contact.email != null && contact.emails.contains('@')
Ich könnte eine kleine Methode wie diese schreiben
private Boolean isEmailValid(String email){...}
- Ersetzen eines Inline-Kommentars durch eine andere private Methode, sodass der Methodenname sich selbst beschreibt und kein Inline-Kommentar darauf steht
- Eine Klasse sollte nur einen Grund haben, sich zu ändern
Und ein paar andere. Der Punkt ist, dass was eine Methode mit 30 Zeilen sein könnte, eine Klasse wird, wegen der winzigen Methoden, die Kommentare ersetzen und Bedingungen usw. einkapseln Füge die gesamte Funktionalität in eine Klasse ein, wenn es eigentlich eine Methode hätte sein sollen.
Mir ist bewusst, dass jede extreme Praxis schädlich sein kann.
Die konkrete Frage, auf die ich eine Antwort suche, lautet:
Ist dies ein akzeptables Nebenprodukt beim Schreiben von sauberem Code? Wenn ja, mit welchen Argumenten kann ich die Tatsache rechtfertigen, dass mehr LOC geschrieben wurden?
Die Organisation befasst sich nicht speziell mit mehr LOC, aber mehr LOC kann zu sehr großen Klassen führen (die wiederum aus Gründen der Lesbarkeit durch eine lange Methode ohne eine Reihe von Funktionen ersetzt werden könnten, die nur einmal verwendet werden können).
Wenn Sie eine Klasse sehen, die groß genug ist, entsteht der Eindruck, dass die Klasse beschäftigt genug ist und ihre Verantwortung abgeschlossen wurde. Sie könnten daher am Ende mehr Klassen erstellen, um andere Funktionen zu erreichen. Das Ergebnis sind dann viele Klassen, die alle mit Hilfe vieler kleiner Hilfsmethoden "eins" machen.
Dies ist das besondere Anliegen ... diese Klassen könnten eine einzelne Klasse sein, die ohne die Hilfe vieler kleiner Methoden immer noch "eins" erreicht. Es könnte eine einzelne Klasse mit vielleicht 3 oder 4 Methoden und einigen Kommentaren sein.
quelle
Antworten:
Diese Leute haben etwas richtig identifiziert: Sie möchten, dass der Code einfacher zu pflegen ist. Wenn sie jedoch einen Fehler gemacht haben, wird davon ausgegangen, dass die Wartung umso einfacher ist, je weniger Code vorhanden ist.
Damit Code einfach zu warten ist, muss er einfach zu ändern sein. Der mit Abstand einfachste Weg, einfach zu ändernden Code zu erhalten, besteht darin, eine vollständige Reihe automatisierter Tests durchzuführen, die fehlschlagen, wenn Ihre Änderung nicht erfolgreich ist. Tests sind Code, sodass das Schreiben dieser Tests Ihre Codebasis anschwellen lässt. Und das ist gut so.
Zweitens, um herauszufinden, was geändert werden muss, muss Ihr Code sowohl gut lesbar als auch verständlich sein. Es ist sehr unwahrscheinlich, dass sehr knapper Code, der nur verkleinert wird, um den Zeilenzähler niedrig zu halten, einfach zu lesen ist. Es ist offensichtlich ein Kompromiss zu schließen, da längerer Code länger zum Lesen braucht. Aber wenn es schneller zu verstehen ist, dann lohnt es sich. Wenn es diesen Vorteil nicht bietet, dann ist diese Ausführlichkeit kein Vorteil mehr. Aber wenn längerer Code die Lesbarkeit verbessert, ist dies wiederum eine gute Sache.
quelle
Ja, es ist ein akzeptables Nebenprodukt, und die Rechtfertigung ist, dass es jetzt so aufgebaut ist, dass Sie den größten Teil des Codes nicht die meiste Zeit lesen müssen. Anstatt jedes Mal, wenn Sie eine Änderung vornehmen, eine 30-Zeilen-Funktion zu lesen, lesen Sie eine 5-Zeilen-Funktion, um den Gesamtfluss zu ermitteln, und möglicherweise ein paar Hilfsfunktionen, wenn Ihre Änderung diesen Bereich berührt. Wenn Ihre neue "zusätzliche" Klasse aufgerufen wird
EmailValidator
und Sie wissen, dass Ihr Problem nicht mit der E-Mail-Validierung zusammenhängt, können Sie das Lesen insgesamt überspringen.Es ist auch einfacher, kleinere Teile wiederzuverwenden, wodurch sich die Zeilenanzahl für Ihr Gesamtprogramm tendenziell verringert. Eine
EmailValidator
kann überall verwendet werden. Einige Codezeilen, die eine E-Mail-Überprüfung durchführen, jedoch zusammen mit dem Datenbankzugriffscode verarbeitet werden, können nicht wiederverwendet werden.Und dann überlegen Sie, was zu tun ist, wenn die E-Mail-Überprüfungsregeln jemals geändert werden müssen. oder viele Orte, möglicherweise fehlen ein paar?
quelle
iterations < _maxIterations
in eine aufgerufene Methode zu ziehen,ShouldContinueToIterate
ist dumm .Bill Gates wurde bekanntermaßen die Aussage zugeschrieben, dass "das Messen des Programmierfortschritts durch Codezeilen dem Messen des Fortschritts beim Flugzeugbau nach Gewicht gleicht."
Ich stimme diesem Gefühl demütig zu. Das soll nicht heißen, dass ein Programm mehr oder weniger Codezeilen anstreben sollte, aber dass dies letztendlich nicht zählt, um ein funktionierendes und funktionierendes Programm zu erstellen. Es ist hilfreich, sich daran zu erinnern, dass der Grund für das Hinzufügen zusätzlicher Codezeilen letztendlich darin besteht, dass es auf diese Weise theoretisch besser lesbar ist.
Es kann zu Meinungsverschiedenheiten darüber kommen, ob eine bestimmte Änderung mehr oder weniger lesbar ist, aber ich glaube nicht, dass Sie sich irren, wenn Sie eine Änderung an Ihrem Programm vornehmen, weil Sie der Meinung sind, dass Sie damit die Lesbarkeit verbessern. Zum Beispiel eines machen
isEmailValid
könnte als überflüssig und unnötig gedacht werden, vor allem , wenn es durch die Klasse genau einmal aufgerufen wird, die es definiert. Ich würde jedoch lieberisEmailValid
eine Bedingung als eine Folge von UND-verknüpften Bedingungen sehen, wobei ich bestimmen muss, was jede einzelne Bedingung prüft und warum sie geprüft wird.Sie geraten in Schwierigkeiten, wenn Sie eine
isEmailValid
Methode erstellen , die Nebenwirkungen hat oder andere Dinge als die E-Mail überprüft, da dies schlimmer ist, als einfach alles aufzuschreiben. Es ist schlimmer, weil es irreführend ist und ich möglicherweise einen Fehler verpasse.Obwohl Sie dies in diesem Fall eindeutig nicht tun, möchte ich Sie ermutigen, so fortzufahren, wie Sie es tun. Sie sollten sich immer fragen, ob die Änderung lesbarer ist, und wenn dies der Fall ist, tun Sie es!
quelle
Hier geht es darum, das eigentliche Ziel aus den Augen zu verlieren.
Was zählt, ist die Verkürzung der Entwicklungsstunden . Das wird in Zeit (oder gleichwertigem Aufwand) gemessen, nicht in Codezeilen.
Dies ist so, als würde man sagen, dass die Autohersteller ihre Autos mit weniger Schrauben bauen sollten, da das Einschrauben einer Schraube nicht null Zeit kostet. Das ist zwar pedantisch korrekt, der Marktwert eines Autos wird jedoch nicht durch die Anzahl der Schrauben definiert oder nicht. Ein Auto muss vor allem leistungsfähig, sicher und wartungsfreundlich sein.
Der Rest der Antwort ist ein Beispiel dafür, wie sauberer Code zu Zeitgewinnen führen kann.
Protokollierung
Nimm eine Anwendung (A) ohne Protokollierung. Erstellen Sie nun die Anwendung B, die dieselbe Anwendung A ist, jedoch mit Protokollierung. B wird immer mehr Codezeilen haben, und daher müssen Sie mehr Code schreiben.
Aber viel Zeit wird darauf verwendet, Probleme und Fehler zu untersuchen und herauszufinden, was schief gelaufen ist.
Bei Anwendung A können Entwickler den Code nicht mehr lesen und müssen das Problem fortlaufend reproduzieren und den Code schrittweise durchgehen, um die Ursache des Problems zu ermitteln. Dies bedeutet, dass der Entwickler von Beginn der Ausführung bis zum Ende in jeder verwendeten Ebene testen und jede verwendete Logik beobachten muss.
Vielleicht hat er das Glück, es sofort zu finden, aber vielleicht wird die Antwort an der letzten Stelle sein, die er sich vorstellt.
Bei Anwendung B kann ein Entwickler unter der Annahme einer perfekten Protokollierung die Protokolle beobachten, die fehlerhafte Komponente sofort identifizieren und weiß nun, wo er suchen muss.
Dies kann eine Frage von Minuten, Stunden oder Tagen sein; abhängig von der Größe und Komplexität der Codebasis.
Regressionen
Nehmen Sie die Anwendung A, die überhaupt nicht trocken ist.
Nehmen Sie die Anwendung B, die DRY ist, aber aufgrund der zusätzlichen Abstraktionen mehr Zeilen benötigt.
Es wird eine Änderungsanforderung eingereicht, die eine Änderung der Logik erfordert.
Für Anwendung B ändert der Entwickler die (eindeutige, gemeinsam genutzte) Logik entsprechend der Änderungsanforderung.
Für Anwendung A muss der Entwickler alle Instanzen dieser Logik ändern, in denen er sich daran erinnert, dass sie verwendet wird.
Dies kann zu einer enormen Zeitverschwendung führen. Nicht nur in der Entwicklung, sondern auch beim Jagen und Finden des Fehlers. Die Anwendung kann sich auf eine Weise unregelmäßig verhalten, die Entwickler nicht leicht nachvollziehen können. Dies führt zu längeren Debugsitzungen.
Entwickleraustauschbarkeit
Entwickler Eine erstellte Anwendung A. Der Code ist weder sauber noch lesbar, funktioniert jedoch wie ein Zauber und wurde in der Produktion ausgeführt. Es überrascht auch nicht, dass es keine Dokumentation gibt.
Entwickler A fehlt wegen Feiertagen für einen Monat. Ein Notfalländerungsantrag wird gestellt. Es kann nicht länger als drei Wochen dauern, bis Dev A zurückkommt.
Entwickler B muss diese Änderung durchführen. Er muss nun die gesamte Codebasis lesen, verstehen, wie alles funktioniert, warum es funktioniert und was es zu erreichen versucht. Das dauert ewig, aber sagen wir mal, er schafft es in drei Wochen.
Zur gleichen Zeit hat die Anwendung B (die Entwickler B erstellt hat) einen Notfall. Dev B ist belegt, aber Dev C ist verfügbar, obwohl er die Codebasis nicht kennt. Was machen wir?
Dev A kommt aus seinem Urlaub zurück und stellt fest, dass B den Code nicht verstanden und ihn daher schlecht implementiert hat. Es ist nicht B's Schuld, weil er alle verfügbaren Ressourcen genutzt hat, der Quellcode war einfach nicht ausreichend lesbar. Muss A nun Zeit damit verbringen, die Lesbarkeit des Codes zu verbessern?
All diese und viele andere Probleme verschwenden Zeit . Ja, kurzfristig erfordert sauberer Code jetzt mehr Aufwand , aber es wird sich in Zukunft auszahlen, wenn unvermeidliche Fehler / Änderungen behoben werden müssen.
Das Management muss verstehen, dass eine kurze Aufgabe in Zukunft mehrere lange Aufgaben erspart. Zu versagen zu planen ist planen zu versagen.
Meine goto Erklärung fragt das Management, was es bevorzugen würde: eine Anwendung mit einer 100KLOC-Codebasis, die in drei Monaten entwickelt werden kann, oder eine 50KLOC-Codebasis, die in sechs Monaten entwickelt werden kann.
Sie werden offensichtlich die kürzere Entwicklungszeit wählen, weil sich das Management nicht um KLOC kümmert . Manager, die sich auf KLOC konzentrieren, sind Mikromanager, während sie nicht wissen, was sie zu verwalten versuchen.
quelle
Ich denke, Sie sollten sehr vorsichtig sein, wenn Sie "Clean Code" -Praktiken anwenden, die zu einer größeren Gesamtkomplexität führen. Vorzeitiges Refactoring ist die Wurzel vieler schlechter Dinge.
Extrahieren einen bedingte zu einer Funktion führen zu einem einfacheren Code an der Stelle , wo die bedingten von extrahiert , führen aber zu mehr Gesamtkomplexität , weil Sie jetzt eine Funktion haben , die von mehreren Stellen im Programm zu sehen ist. Sie fügen allen anderen Funktionen, bei denen diese neue Funktion jetzt sichtbar ist, eine leichte Komplexitätsbelastung hinzu.
Ich sage nicht, dass Sie die Bedingung nicht extrahieren sollten , nur, dass Sie sorgfältig überlegen sollten, wenn Sie müssen.
In all dem ist das ein Grund dafür, dass die Extraktion darüber hinaus nur "sauberer Code" ist. Darüber hinaus würden Sie wahrscheinlich nicht einmal im Zweifel sein, ob es das Richtige war.
Ich würde sagen, wenn Sie Zweifel haben, wählen Sie immer den einfachsten und direktesten Code.
quelle
Ich möchte darauf hinweisen, dass daran nichts grundsätzlich falsch ist:
Zumindest unter der Annahme, dass es dieses Mal verwendet wird.
Ich könnte sehr leicht Probleme damit haben:
Ein paar Dinge, auf die ich achten würde:
Wenn es einmal verwendet wird, einfach zu analysieren ist und weniger als eine Zeile benötigt, würde ich die Entscheidung als Zweites erraten. Es ist wahrscheinlich nichts, was ich anrufen würde, wenn es kein spezielles Problem eines Teams wäre.
Andererseits habe ich gesehen, dass Methoden so etwas tun:
Dieses Beispiel ist offensichtlich nicht TROCKEN.
Oder auch nur diese letzte Aussage kann ein anderes Beispiel geben:
Das Ziel sollte sein, den Code lesbarer zu machen:
Ein anderes Szenario:
Möglicherweise haben Sie eine Methode wie:
Wenn dies zu Ihrer Geschäftslogik passt und nicht wiederverwendet wird, gibt es hier kein Problem.
Aber wenn jemand fragt "Warum wird '@' gespeichert, weil das nicht stimmt!" und Sie beschließen, eine tatsächliche Validierung hinzuzufügen und dann zu extrahieren!
Sie werden froh sein, dass Sie dies getan haben, als Sie auch das zweite E-Mail-Konto des Präsidenten Pr3 $ sid3nt @ h0m3! @ Mydomain.com registrieren mussten, und sich dafür entscheiden, RFC 2822 zu testen und zu unterstützen.
Zur Lesbarkeit:
Wenn Ihr Code so klar ist, brauchen Sie hier keine Kommentare. In der Tat brauchen Sie keine Kommentare, um zu sagen, was der Code die meiste Zeit tut, sondern warum er es tut:
Ob die Kommentare über einer if-Anweisung oder innerhalb einer winzigen Methode, ist für mich umständlich. Ich könnte sogar das Gegenteil von nützlich mit guten Kommentaren in einer anderen Methode argumentieren, da Sie jetzt zu einer anderen Methode navigieren müssten, um zu sehen, wie und warum sie das tut, was sie tut.
Zusammenfassend: Messen Sie diese Dinge nicht; Konzentrieren Sie sich auf die Prinzipien, aus denen der Text besteht (DRY, SOLID, KISS).
quelle
Whether the comments above an if statement or inside a tiny method is to me, pedantic.
Dies ist ein "Strohhalm, der dem Kamel den Rücken gebrochen hat" -Problem. Sie haben Recht, dass diese eine Sache nicht besonders schwer zu lesen ist. Aber wenn Sie eine große Methode (zB ein großer Import) , die Dutzende dieser kleinen Auswertungen hat, diese in lesbaren Methodennamen eingekapselten (IsUserActive
,GetAverageIncome
,MustBeDeleted
, ...) wird eine spürbare Verbesserung wird den Code beim Lesen. Das Problem mit dem Beispiel ist, dass es nur einen Strohhalm beobachtet, nicht das gesamte Bündel, das dem Kamel den Rücken bricht.if (contact.email != null && contact.email.contains('@'))
ist fehlerhaft. Wenn das if false ist, kann keine der else if-Zeilen true sein. Dies ist imLooksSortaLikeAnEmail
Block überhaupt nicht sichtbar . Eine Funktion, die eine einzelne Codezeile enthält, ist nicht viel besser als ein Kommentar, der erklärt, wie die Zeile funktioniert.Clean Code ist ein ausgezeichnetes Buch, das es wert ist, gelesen zu werden, aber es ist nicht die letzte Instanz in solchen Angelegenheiten.
Das Aufteilen von Code in logische Funktionen ist in der Regel eine gute Idee, aber nur wenige Programmierer tun dies in dem Maße, wie es Martin tut. Irgendwann wird es immer weniger, wenn alles in Funktionen umgewandelt wird Stücke.
Wenn es sich nicht lohnt, eine neue Funktion zu erstellen, können Sie einfach eine Zwischenvariable verwenden:
Auf diese Weise bleibt der Code übersichtlich, ohne dass Sie viel in der Datei herumspringen müssen.
Ein weiteres Problem ist, dass Clean Code als Buch mittlerweile ziemlich alt ist. Viel Software-Engineering hat sich in Richtung funktionale Programmierung bewegt, während Martin alles unternimmt, um den Dingen Zustand zu verleihen und Objekte zu schaffen. Ich vermute, er hätte ein ganz anderes Buch geschrieben, wenn er es heute geschrieben hätte.
quelle
In Anbetracht der Tatsache, dass die Bedingung "ist E-Mail gültig", die Sie derzeit haben, die sehr ungültige E-Mail-Adresse "
@
" akzeptieren würde, denke ich, dass Sie allen Grund haben, eine EmailValidator-Klasse zu abstrahieren. Verwenden Sie noch besser eine gut getestete Bibliothek, um E-Mail-Adressen zu validieren.Codezeilen als Metrik sind bedeutungslos. Die wichtigen Fragen im Software Engineering sind nicht:
Die wichtigen Fragen sind:
Ich habe nie an LoC gedacht, wenn ich Code für einen anderen Zweck als Code Golf schreibe. Ich habe mich gefragt: "Könnte ich das präziser schreiben?", Aber aus Gründen der Lesbarkeit, Wartbarkeit und Effizienz, nicht nur der Länge.
Sicher, vielleicht könnte ich eine lange Kette von Booleschen Operationen anstelle einer Utility-Methode verwenden, aber sollte ich das tun?
Ihre Frage lässt mich tatsächlich an einige lange Ketten von Booleschen Werten zurückdenken, die ich geschrieben habe, und erkennen, dass ich stattdessen wahrscheinlich eine oder mehrere Dienstprogrammmethoden hätte schreiben sollen.
quelle
Auf einer Ebene haben sie Recht - weniger Code ist besser. Eine andere Antwort zitiert Gate, ich bevorzuge:
Kurz gesagt, je weniger Code Sie haben, desto weniger kann schief gehen. Wenn etwas nicht nötig ist, dann schneide es.
Wenn der Code zu kompliziert ist, vereinfachen Sie ihn, bis nur noch die eigentlichen Funktionselemente übrig sind.
Wichtig hierbei ist, dass sich alle auf die Funktionalität beziehen und nur das dafür erforderliche Minimum haben. Es sagt nichts darüber aus, wie das ausgedrückt wird.
Was Sie tun, wenn Sie versuchen, sauberen Code zu haben, widerspricht dem oben Gesagten nicht. Sie erweitern Ihr LOC, fügen jedoch keine nicht verwendeten Funktionen hinzu.
Das Endziel ist lesbarer Code, aber keine überflüssigen Extras. Die beiden Prinzipien sollten nicht gegeneinander wirken.
Eine Metapher würde ein Auto bauen. Der funktionale Teil des Codes ist das Fahrgestell, der Motor, die Räder ... was das Auto zum Laufen bringt. Wie Sie das aufbrechen, ist mehr wie die Aufhängung, Servolenkung und so weiter, es macht es einfacher zu handhaben. Sie möchten, dass Ihre Mechaniker so einfach wie möglich sind, während sie ihre Arbeit ausführen, um das Risiko von Fehlfunktionen zu minimieren. Dies hindert Sie jedoch nicht daran, schöne Sitze zu haben.
quelle
In den vorhandenen Antworten steckt viel Weisheit, aber ich möchte noch einen weiteren Faktor hinzufügen: die Sprache .
Einige Sprachen benötigen mehr Code als andere, um den gleichen Effekt zu erzielen. Insbesondere während Java (von dem ich vermute, dass es die fragliche Sprache ist) sehr bekannt und im Allgemeinen sehr solide und klar und unkompliziert ist, sind einige modernere Sprachen viel prägnanter und aussagekräftiger.
In Java kann es beispielsweise leicht sein, dass 50 Zeilen erforderlich sind, um eine neue Klasse mit drei Eigenschaften zu schreiben, von denen jede einen Getter und einen Setter sowie einen oder mehrere Konstruktoren enthält - während Sie in einer einzelnen Zeile von Kotlin * oder Scala genau dasselbe erreichen können. (Noch größere Einsparung , wenn man will auch geeignet
equals()
,hashCode()
undtoString()
Methoden.)Das Ergebnis ist, dass Sie in Java aufgrund der zusätzlichen Arbeit mit größerer Wahrscheinlichkeit ein allgemeines Objekt wiederverwenden, das nicht wirklich passt, um Eigenschaften in vorhandene Objekte zu pressen oder eine Reihe von "nackten" Eigenschaften einzeln weiterzugeben. Während Sie in einer prägnanten, ausdrucksstarken Sprache arbeiten, schreiben Sie mit größerer Wahrscheinlichkeit besseren Code.
(Dies hebt den Unterschied zwischen der Komplexität der 'Oberfläche' des Codes und der Komplexität der Ideen / Modelle / Verarbeitungen hervor, die er implementiert. Codezeilen sind kein schlechtes Maß für die erste, sondern haben viel weniger mit der zweiten zu tun .)
Die „Kosten“ für die richtige Vorgehensweise hängen also von der Sprache ab. Vielleicht ist ein Zeichen für eine gute Sprache eines, bei dem man sich nicht zwischen einer guten und einer einfachen Sprache entscheidet!
(* Dies ist nicht wirklich der richtige Ort für einen Stecker, aber Kotlin ist meiner Meinung nach einen Blick wert.)
quelle
Nehmen wir an, Sie arbeiten gerade mit der Klasse
Contact
. Die Tatsache, dass Sie eine andere Methode zur Validierung der E-Mail-Adresse schreiben, ist ein Beweis dafür, dass die KlasseContact
keine einzige Verantwortung übernimmt.Es übernimmt auch einige E-Mail-Aufgaben, die im Idealfall eine eigene Klasse sein sollten.
Ein weiterer Beweis dafür, dass Ihr Code eine Fusion von
Contact
undEmail
Klasse ist, ist, dass Sie den E-Mail-Validierungscode nicht einfach testen können. Es erfordert viele Manöver, um den E-Mail-Validierungscode in einer großen Methode mit den richtigen Werten zu erreichen. Siehe die Methode unten.Wenn Sie andererseits eine separate E-Mail-Klasse mit einer Methode zur E-Mail-Validierung hätten, würden Sie zum Testen Ihres Validierungscodes nur einen einfachen Anruf
Email.Validation()
mit Ihren Testdaten tätigen .Bonusinhalt : MFeathers Vortrag über die tiefe Synergie zwischen Testbarkeit und gutem Design.
quelle
Es wurde festgestellt, dass eine Verringerung des LOC mit verringerten Defekten korreliert , sonst nichts. Angenommen, Sie haben jedes Mal, wenn Sie den LOC verringern, die Wahrscheinlichkeit von Fehlern verringert, geraten Sie im Wesentlichen in die Falle der Annahme, dass Korrelation gleich Kausalität ist. Der reduzierte LOC ist das Ergebnis guter Entwicklungspraktiken und nicht das, was Code gut macht.
Nach meiner Erfahrung sind Leute, die ein Problem mit weniger Code (auf Makroebene) lösen können, tendenziell geschickter als diejenigen, die mehr Code schreiben, um dasselbe zu tun. Was diese erfahrenen Entwickler tun, um die Codezeilen zu reduzieren, ist die Verwendung / Erstellung von Abstraktionen und wiederverwendbaren Lösungen, um allgemeine Probleme zu lösen. Sie verbringen keine Zeit damit, Codezeilen zu zählen und sich darüber Gedanken zu machen, ob sie hier oder da eine Zeile abschneiden können. Oft ist der Code, den sie schreiben, ausführlicher als nötig, sie schreiben nur weniger davon.
Lassen Sie mich Ihnen ein Beispiel geben. Ich musste mich mit der Logik von Zeiträumen auseinandersetzen und wie sie sich überlappen, ob sie benachbart sind und welche Lücken zwischen ihnen bestehen. Als ich anfing, an diesen Problemen zu arbeiten, musste ich überall Codeblöcke verwenden, um die Berechnungen durchzuführen. Schließlich erstellte ich Klassen, um die Zeiträume und Operationen darzustellen, die Überschneidungen, Ergänzungen usw. berechneten. Dies entfernte sofort große Mengen an Code und wandelte sie in einige Methodenaufrufe um. Aber diese Klassen selbst waren überhaupt nicht knapp geschrieben.
Einfach ausgedrückt: Wenn Sie versuchen, LOC zu reduzieren, indem Sie versuchen, eine Codezeile hier oder da mit knapperem Code zu schneiden, machen Sie es falsch. Es ist, als würde man versuchen, Gewicht zu verlieren, indem man die Menge an Gemüse reduziert, die man isst. Schreiben Sie Code, der durch Wiederverwendung und Abstraktion einfach zu verstehen, zu warten und zu debuggen und zu reduzieren ist.
quelle
Sie haben einen gültigen Kompromiss gefunden
Es gibt also in der Tat einen Kompromiss, der der gesamten Abstraktion inhärent ist . Wenn jemand versucht, N Codezeilen in seine eigene Funktion zu ziehen, um sie zu benennen und zu isolieren, erleichtert dies gleichzeitig das Lesen der aufrufenden Site (indem er sich auf einen Namen bezieht und nicht auf alle wichtigen Details, die diesem Namen zugrunde liegen) und komplexer (Sie haben jetzt eine Bedeutung, die sich über zwei verschiedene Teile der Codebasis erstreckt). "Leicht" ist das Gegenteil von "Schwer", aber es ist kein Synonym für "Einfach", was das Gegenteil von "Komplex" ist. Die beiden sind keine Gegensätze, und die Abstraktion erhöht immer die Komplexität, um die eine oder andere Form der Leichtigkeit einzufügen.
Wir können die zusätzliche Komplexität direkt erkennen, wenn eine Änderung der Geschäftsanforderungen zu einer Undichtigkeit der Abstraktion führt. Vielleicht wäre eine neue Logik am natürlichsten in der Mitte des vorab abstrahierten Codes verlaufen, zum Beispiel wenn der abstrahierte Code einen Baum durchquert und Sie wirklich eine Art von Information sammeln (und vielleicht darauf reagieren) möchten, während Sie sind den Baum überqueren. Wenn Sie diesen Code abstrahiert haben, gibt es möglicherweise andere Aufrufseiten, und das Hinzufügen der erforderlichen Logik in der Mitte der Methode kann diese anderen Aufrufseiten beschädigen. Sehen Sie, wann immer wir eine Codezeile ändern, müssen wir uns nur den unmittelbaren Kontext dieser Codezeile ansehen. Wenn wir eine Methode ändern, müssen wir unseren gesamten Quellcode mit Cmd-F abarbeiten, um nach irgendetwas zu suchen, das durch die Änderung des Vertrags dieser Methode kaputt gehen könnte.
Der Greedy-Algorithmus kann in diesen Fällen fehlschlagen
Die Komplexität hat auch dazu geführt, dass der Code in gewissem Sinne weniger lesbar ist als mehr. In einem früheren Job habe ich mich mit einer HTTP-API befasst, die sehr sorgfältig und präzise in mehrere Ebenen unterteilt war. Jeder Endpunkt wird von einem Controller festgelegt, der die Form der eingehenden Nachricht überprüft und sie dann an einen Manager der "Geschäftslogikebene" weitergibt , der dann eine Anfrage an eine "Datenschicht" stellte, die für mehrere Abfragen an eine "Datenzugriffsobjekt" -Schicht verantwortlich war, die für die Erstellung mehrerer SQL-Delegaten verantwortlich war, die Ihre Frage tatsächlich beantworteten. Das erste, was ich dazu sagen kann, war, dass 90% des Codes Copy-and-Paste-Boilerplate waren, mit anderen Worten: No-Ops. In vielen Fällen war das Lesen einer bestimmten Code-Passage also sehr "einfach", weil "oh, dieser Manager leitet die Anforderung nur an dieses Datenzugriffsobjekt weiter".Viele Kontextwechsel und das Auffinden von Dateien und der Versuch, Informationen zu verfolgen, die Sie niemals hätten verfolgen sollen. "Dies wird auf dieser Ebene als X bezeichnet. Auf dieser anderen Ebene wird es als X 'bezeichnet. Dann wird es in dieser anderen Ebene als X' 'bezeichnet andere Schicht. "
Ich denke, als ich aufgehört habe, befand sich diese einfache CRUD-API in einem Stadium, in dem, wenn man sie mit 30 Zeilen pro Seite druckte, 10 bis 20 fünfhundertseitige Lehrbücher in einem Regal Platz fanden: Es war eine ganze Enzyklopädie von Wiederholungen Code. In Bezug auf die essentielle Komplexität bin ich mir nicht sicher, ob es überhaupt ein halbes Lehrbuch von essentieller Komplexität gab. Wir hatten nur vielleicht 5-6 Datenbankdiagramme, um damit umzugehen. Es war ein gewaltiges Unterfangen, zu lernen, dass es sich um ein gewaltiges Unterfangen handelte, und neue Funktionen hinzuzufügen, wurde so schmerzhaft, dass wir tatsächlich Vorlagen für die Erstellung von Boilerplates hatten, mit denen wir neue Funktionen hinzufügen würden.
Ich habe also aus erster Hand gesehen, wie die Tatsache, dass jeder Teil sehr lesbar und offensichtlich ist, dazu führen kann, dass das Ganze sehr unlesbar und nicht offensichtlich ist. Dies bedeutet, dass der Greedy-Algorithmus fehlschlagen kann. Sie kennen den gierigen Algorithmus, ja? "Ich werde jeden Schritt tun, um die Situation vor Ort am besten zu verbessern, und dann werde ich darauf vertrauen, dass ich mich in einer global verbesserten Situation befinde." Es ist oft ein schöner erster Versuch, kann aber auch in komplexen Zusammenhängen fehlen. Zum Beispiel könnten Sie in der Fertigung versuchen, die Effizienz jedes einzelnen Schritts in einem komplexen Fertigungsprozess zu steigern - machen Sie größere Chargen, schreien Sie Leute auf dem Boden an, die scheinbar nichts tun, um ihre Hände mit etwas anderem zu beschäftigen - und Dies kann oft die globale Effizienz des Systems zerstören.
Best Practice: Verwenden Sie DRY und Längen, um den Anruf zu tätigen
(Hinweis: Dieser Abschnittstitel ist ein Witz. Ich sage meinen Freunden oft, dass sie 90% der Zeit nicht über etwas wie SQL-Injection oder Kennwort-Hashing sprechen , wenn jemand sagt, wir sollten X tun, weil dies nach bewährten Methoden möglich ist.) oder was auch immer - einseitige Best Practices - und so kann die Aussage in 90% der Fälle in "Wir sollten X tun, weil ich es sage" übersetzt werden . Als hätten sie vielleicht einen Blog-Artikel von einem Unternehmen, das einen besseren Job gemacht hat mit X statt mit X ', aber es gibt im Allgemeinen keine Garantie dafür, dass Ihr Geschäft diesem Geschäft ähnelt, und es gibt im Allgemeinen einen anderen Artikel aus einem anderen Geschäft, der mit X' statt mit X einen besseren Job gemacht hat. Nehmen Sie also bitte nicht auch den Titel Ernsthaft.)
Was ich empfehlen würde, basiert auf einem Vortrag von Jack Diederich mit dem Titel Stop Writing Classes (youtube.com) . In diesem Vortrag bringt er einige wichtige Punkte zum Ausdruck: Zum Beispiel, dass man weiß, dass eine Klasse wirklich nur eine Funktion ist, wenn sie nur zwei öffentliche Methoden hat, und eine davon ist der Konstruktor / Initialisierer. Aber in einem Fall spricht er darüber, wie eine hypothetische Bibliothek, die er für den Vortrag durch einen String ersetzt hat, als "Muffin" eine eigene Klasse "MuffinHash" deklarierte, die eine Unterklasse des in
dict
Python eingebauten Typs war. Die Implementierung war völlig leer - jemand hatte nur gedacht: "Möglicherweise müssen wir später Python-Wörterbücher um benutzerdefinierte Funktionen erweitern. Lassen Sie uns jetzt für alle Fälle eine Abstraktion einführen."Und seine trotzige Antwort war einfach: "Wir können das immer später tun, wenn wir müssen."
Ich glaube, wir tun manchmal so, als wären wir in Zukunft schlechtere Programmierer als jetzt. Vielleicht möchten wir eine Kleinigkeit einfügen, die uns in Zukunft glücklich macht. Wir antizipieren die Bedürfnisse der Zukunft. "Wenn der Datenverkehr 100-mal so groß ist, wie wir glauben, lässt sich dieser Ansatz nicht skalieren. Daher müssen wir die Anfangsinvestition in diesen schwierigeren Ansatz investieren, der skalieren wird." Sehr verdächtig.
Wenn wir diesen Rat ernst nehmen, müssen wir herausfinden, wann "später" gekommen ist. Am naheliegendsten wäre es wahrscheinlich, aus Stilgründen eine Obergrenze für die Länge der Dinge festzulegen. Und ich denke, der beste Rat wäre, DRY zu verwenden - wiederholen Sie sich nicht - mit diesen Heuristiken über die Leitungslängen, um ein Loch in die SOLID-Prinzipien zu flicken. Basierend auf der Heuristik von 30 Zeilen, die eine "Seite" von Text und eine Analogie mit Prosa ist,
Wie ich oben bereits erwähnt habe, habe ich diese Statistiken anhand des aktuellen Linux-Quelltextbaums getestet, um die ungefähren Prozentsätze zu ermitteln, aber sie sind in der literarischen Analogie auch logisch.
quelle