Wie rechtfertigen Sie das Schreiben von mehr Code, indem Sie die Clean Code-Praktiken befolgen?

106

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.

CommonCoreTawan
quelle
98
Wenn Ihre Organisation nur LOC als Metrik für Ihre Codebasen verwendet, ist es zunächst hoffnungslos, sauberen Code zu rechtfertigen.
Kilian Foth
24
Wenn Wartbarkeit Ihr Ziel ist, ist LOC nicht die beste Messgröße - es ist eine davon, aber es gibt weit mehr zu berücksichtigen, als es einfach kurz zu halten.
Zibbobz
29
Es gibt keine Antwort, sondern einen Punkt, der beachtet werden muss: Es gibt eine ganze Subcommunity, die Code mit so wenig Zeilen / Symbolen wie möglich schreibt. codegolf.stackexchange.com Man kann argumentieren, dass die meisten Antworten dort nicht so lesbar sind, wie sie sein könnten.
Antitheos
14
Lernen Sie die Gründe für jedes Best Practice kennen und regeln Sie nicht nur selbst. Das Befolgen von Regeln ohne Begründung ist Cargo Kult. Jede einzelne Regel hat einen eigenen Grund.
Gherman
9
Nebenbei bemerkt, und wenn Sie Ihr Beispiel verwenden, werden Sie manchmal denken, wenn Sie Dinge auf Methoden ausdrücken: "Vielleicht gibt es eine Bibliotheksfunktion, die dies tun kann". Um beispielsweise eine E-Mail-Adresse zu validieren, können Sie eine System.Net.Mail.MailAddress erstellen, die diese für Sie validiert. Sie können dann (hoffentlich) dem Autor dieser Bibliothek vertrauen, dass er es richtig macht. Dies bedeutet, dass Ihre Codebasis mehr Abstraktionen aufweist und kleiner wird.
Gregory Currie

Antworten:

130

... wir sind ein sehr kleines Team, das eine relativ große und undokumentierte Codebasis unterstützt (die wir geerbt haben), sodass es für einige Entwickler / Manager von Nutzen ist, weniger Code zu schreiben, um die Dinge zu erledigen, sodass weniger Code zu warten ist

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.

David Arno
quelle
27
"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." Das stimmt einfach nicht. Tests erfordern zusätzliche Arbeit für jede Verhaltensänderung, da die Tests ebenfalls geändert werden müssen . Dies ist beabsichtigt und viele argumentieren, dass sie die Änderung sicherer machen, aber es macht Änderungen notwendigerweise schwieriger.
Jack Aidley
63
Sicher, aber die Zeit, die Sie für die Wartung dieser Tests verlieren, ist um ein Vielfaches geringer als die Zeit, in der Sie die Diagnose und Behebung von Fehlern verloren hätten, die durch die Tests verhindert werden.
MetaFight
29
@JackAidley, das Ändern der Tests zusammen mit dem Code kann den Anschein von mehr Arbeit erwecken, aber nur, wenn man die schwer zu findenden Fehler ignoriert, die Änderungen am ungetesteten Code hervorrufen und die oft erst nach dem Versand gefunden werden . Letzteres bietet lediglich die Illusion von weniger Arbeit.
David Arno
31
@ JackAidley, ich bin völlig anderer Meinung als Sie. Tests erleichtern das Ändern von Code. Ich gebe zu, dass schlecht gestalteter Code, der zu eng an Tests gekoppelt und damit eng an Tests gekoppelt ist, schwer zu ändern ist, aber meiner Erfahrung nach ist gut strukturierter, gut getesteter Code einfach zu ändern.
David Arno
22
@ JackAidley Sie können eine Menge umgestalten, ohne eine API oder Schnittstelle zu ändern. Es bedeutet, dass Sie verrückt werden können, während Sie den Code ändern, ohne eine einzelne Zeile in Einheiten oder Funktionstests ändern zu müssen. Das heißt, wenn Ihre Tests keine bestimmte Implementierung testen.
Eric Duminil
155

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 EmailValidatorund 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 EmailValidatorkann ü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?

Karl Bielefeldt
quelle
10
weitaus bessere Antwort als die anstrengenden "Unit-Tests lösen alle Ihre Probleme"
Dirk Boer
13
Diese Antwort trifft auf einen wichtigen Punkt, den Onkel Bob und seine Freunde immer zu vermissen scheinen - die Umgestaltung in kleine Methoden hilft nur, wenn Sie nicht alle kleinen Methoden lesen müssen, um herauszufinden, was Ihr Code tut. Es ist ratsam, eine separate Klasse zur Validierung von E-Mail-Adressen zu erstellen. Den Code iterations < _maxIterationsin eine aufgerufene Methode zu ziehen, ShouldContinueToIterateist dumm .
BJ Myers
4
@DavidArno: "Nützlich sein"! = "Löst alle Ihre Probleme"
Christian Hackl
2
@DavidArno: Wenn man sich über Leute beschwert, die implizieren, dass Unit-Tests "alle Ihre Probleme lösen", dann meinen sie offensichtlich Leute, die implizieren, dass Unit-Tests fast alle Probleme in der Software-Entwicklung lösen oder zumindest zur Lösung beitragen. Ich denke, niemand wirft irgendjemandem vor, Einheitentests als Mittel zur Beendigung von Krieg, Armut und Krankheit vorzuschlagen. Mit anderen Worten: Die extreme Überbewertung von Unit-Tests in vielen Antworten, nicht nur auf diese Frage, sondern auf SE im Allgemeinen, wird (zu Recht) kritisiert.
Christian Hackl
2
Hallo @DavidArno, mein Kommentar war eindeutig eine Übertreibung, kein Strohmann;) Für mich ist es so: Ich frage, wie ich mein Auto reparieren kann, und religiöse Leute kommen vorbei und sagen mir, ich sollte ein weniger sündiges Leben führen. Theoretisch etwas, das es wert ist, besprochen zu werden, aber es hilft mir nicht wirklich, Autos besser zu machen.
Dirk Boer
34

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 isEmailValidkö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 lieber isEmailValideine 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 isEmailValidMethode 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!

Neil
quelle
1
Das Flugzeuggewicht ist jedoch eine wichtige Messgröße. Und während der Konstruktion wird das zu erwartende Gewicht genau überwacht. Nicht als Zeichen des Fortschritts, sondern als Einschränkung. Die Überwachung von Codezeilen deutet darauf hin, dass mehr besser ist, während im Flugzeugdesign weniger Gewicht besser ist. Also ich denke, Herr Gates hätte eine bessere Illustration für seinen Punkt wählen können.
jos
21
@jos für das jeweilige Team, mit dem OP zusammenarbeitet. Es scheint, dass weniger LOC als "besser" eingestuft wird. Der Punkt, den Bill Gates ansprach, ist, dass LOC in keiner bedeutsamen Weise mit Fortschritt zusammenhängt, genau wie im Flugzeugbau, dass Gewicht in keiner bedeutsamen Weise mit Fortschritt zusammenhängt. Ein Flugzeug im Bau kann relativ schnell 95% seines Endgewichts haben, aber es wäre nur eine leere Hülle ohne Kontrollsysteme, es ist nicht zu 95% vollständig. Dasselbe gilt für Software, wenn ein Programm 100.000 Codezeilen enthält. Dies bedeutet jedoch nicht, dass jeweils 1000 Zeilen 1% der Funktionalität bereitstellen.
Mr.Mindor
7
Fortschrittskontrolle ist eine schwere Aufgabe, nicht wahr? Arme Manager.
jos
@jos: im Code ist es auch besser, weniger Zeilen für die gleiche Funktionalität zu haben, wenn alle anderen gleich sind.
RemcoGerlich
@jos Sorgfältig lesen. Gates sagt nichts darüber aus, ob das Gewicht ein wichtiges Maß für ein Flugzeug selbst ist. Er sagt, dass das Gewicht ein schreckliches Maß für den Fortschritt beim Bau eines Flugzeugs ist. Denn durch diese Maßnahme sind Sie im Grunde genommen fertig, sobald Sie den gesamten Rumpf auf den Boden geworfen haben, da dies voraussichtlich 9x% des Gesamtgewichts des Flugzeugs ausmacht.
Voo
23

Einige Entwickler / Manager sehen daher einen Nutzen darin, weniger Code zu schreiben, um die Dinge zu erledigen, sodass weniger Code zu warten ist

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.

  • Wenn es ihm gelingt, sich alle Instanzen zu merken, muss er dieselbe Änderung dennoch mehrmals implementieren.
  • Wenn er sich nicht alle Instanzen merken kann, haben Sie es jetzt mit einer inkonsistenten Codebasis zu tun, die sich selbst widerspricht. Wenn der Entwickler einen selten verwendeten Code vergessen hat, wird dieser Fehler möglicherweise erst in der Zukunft für den Endbenutzer sichtbar. Werden die Endbenutzer zu diesem Zeitpunkt die Ursache des Problems ermitteln? Auch wenn dies der Fall ist, kann sich der Entwickler möglicherweise nicht erinnern, was die Änderung mit sich gebracht hat, und muss herausfinden, wie diese vergessene Logik geändert werden kann. Vielleicht arbeitet der Entwickler bis dahin noch nicht einmal in der Firma, und dann muss jetzt jemand anderes alles von Grund auf neu herausfinden.

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?

  • Wenn wir B an A arbeiten lassen und C an B arbeiten lassen, haben wir zwei Entwickler, die nicht wissen, was sie tun, und die Arbeit wird suboptimal ausgeführt.
  • Wenn wir B von A abziehen und ihn B ausführen lassen und jetzt C auf A setzen, wird möglicherweise die gesamte Arbeit von Entwickler B (oder ein erheblicher Teil davon) verworfen. Dies ist möglicherweise tage- / wochenlanger Arbeitsaufwand.

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.

Wenn ja, mit welchen Argumenten kann ich die Tatsache rechtfertigen, dass mehr LOC geschrieben wurden?

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.

Flater
quelle
23

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.

  • Wenn Sie die E-Mail-Validierungslogik speziell testen möchten. Dann müssen Sie diese Logik in eine separate Funktion extrahieren - wahrscheinlich sogar in eine Klasse.
  • Wenn dieselbe Logik an mehreren Stellen im Code verwendet wird, müssen Sie sie offensichtlich in eine einzelne Funktion extrahieren. Wiederholen Sie sich nicht!
  • Wenn die Logik offensichtlich in einer separaten Verantwortung liegt, findet die E-Mail-Validierung beispielsweise mitten in einem Sortieralgorithmus statt. Die E-Mail-Überprüfung ändert sich unabhängig vom Sortieralgorithmus, sodass sie in separaten Klassen sein sollten.

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.

JacquesB
quelle
7
Ich muss zustimmen, dass die Umwandlung jeder Bedingung in eine Validierungsmethode zu einer unerwünschten Komplexität bei der Wartung und Codeüberprüfung führen kann. Sie müssen jetzt im Code hin und her wechseln, um sicherzustellen, dass Ihre bedingten Methoden richtig sind. Und was passiert, wenn Sie unterschiedliche Bedingungen für den gleichen Wert haben? Jetzt haben Sie möglicherweise einen Namensalptraum mit mehreren kleinen Methoden, die nur einmal aufgerufen werden und größtenteils gleich aussehen.
pboss3010
7
Einfach die beste Antwort hier. Insbesondere die Beobachtung (im dritten Absatz), dass Komplexität nicht einfach eine Eigenschaft des gesamten Codes als Ganzes ist, sondern etwas, das auf mehreren Abstraktionsebenen gleichzeitig existiert und sich unterscheidet.
Christian Hackl
2
Ich denke, eine Möglichkeit, dies zu formulieren, besteht darin, dass das Extrahieren einer Bedingung im Allgemeinen nur durchgeführt werden sollte, wenn es einen aussagekräftigen, nicht verschleierten Namen für diese Bedingung gibt. Dies ist eine notwendige, aber nicht ausreichende Bedingung.
JimmyJames
Betreff "... weil Sie jetzt eine Funktion haben, die an mehreren Stellen im Programm sichtbar ist" : In Pascal ist es möglich, lokale Funktionen zu haben - "... Jede Prozedur oder Funktion kann ihre eigenen Deklarationen von goto labels, Konstanten haben , Typen, Variablen und andere Prozeduren und Funktionen, ... "
Peter Mortensen
2
@PeterMortensen: Es ist auch in C # und JavaScript möglich. Und das ist großartig! Aber der Punkt bleibt, eine Funktion, sogar eine lokale Funktion, ist in einem größeren Bereich sichtbar als ein Inline-Codefragment.
JacquesB
9

Ich möchte darauf hinweisen, dass daran nichts grundsätzlich falsch ist:

if(contact.email != null && contact.email.contains('@')

Zumindest unter der Annahme, dass es dieses Mal verwendet wird.

Ich könnte sehr leicht Probleme damit haben:

private Boolean isEmailValid(String email){
   return email != null && email.contains('@');
}

Ein paar Dinge, auf die ich achten würde:

  1. Warum ist es privat? Es sieht aus wie ein potenziell nützlicher Stummel. Ist es nützlich genug, eine private Methode zu sein und keine Chance zu haben, dass sie weiter verbreitet wird?
  2. Ich würde die Methode IsValidEmail nicht persönlich nennen, möglicherweise ContainsAtSign oder LooksVaguelyLikeEmailAddress, da sie fast keine echte Validierung durchführt, was vielleicht gut ist, vielleicht nicht das, was erwartet wird.
  3. Wird es mehrmals verwendet?

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:

if (contact.email != null && contact.email.contains('@')) { ... }
else if (contact.email != null && contact.email.contains('@') && contact.email.contains("@mydomain.com")) { //headquarters email }
else if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") ) { //internal contract teams }

Dieses Beispiel ist offensichtlich nicht TROCKEN.

Oder auch nur diese letzte Aussage kann ein anderes Beispiel geben:

if (contact.email != null && contact.email.contains('@') && (contact.email.contains("@news.mydomain.com") || contact.email.contains("@design.mydomain.com") )

Das Ziel sollte sein, den Code lesbarer zu machen:

if (LooksSortaLikeAnEmail(contact.Email)) { ... }
else if (LooksLikeFromHeadquarters(contact.Email)) { ... }
else if (LooksLikeInternalEmail(contact.Email)) { ... }

Ein anderes Szenario:

Möglicherweise haben Sie eine Methode wie:

public void SaveContact(Contact contact){
   if (contact.email != null && contact.email.contains('@'))
   {
       contacts.Add(contact);
       contacts.Save();
   }
}

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:

// If there is an email property and it contains an @ sign then process
if (contact.email != null && contact.email.contains('@'))

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:

// The UI passes '@' by default, the DBA's made this column non-nullable but 
// marketing is currently more concerned with other fields and '@' default is OK
if (contact.email != null && contact.email.contains('@'))

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).

// A valid class that does nothing
public class Nothing 
{

}
AthomSfere
quelle
3
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.
Flater
@Flater und ich hoffen, dass dies der Geist ist, den der Leser davon nimmt.
AthomSfere
1
Diese "Einkapselung" ist ein Anti-Muster, und die Antwort zeigt dies tatsächlich. Wir kommen zurück, um den Code zum Debuggen und zum Erweitern des Codes zu lesen. In beiden Fällen ist es wichtig zu verstehen, was der Code tatsächlich tut. Der Codeblockstart if (contact.email != null && contact.email.contains('@'))ist fehlerhaft. Wenn das if false ist, kann keine der else if-Zeilen true sein. Dies ist im LooksSortaLikeAnEmailBlock ü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.
Quirk
1
Bestenfalls verdeckt eine andere Indirektionsebene die eigentliche Mechanik und erschwert das Debuggen. Im schlimmsten Fall ist der Funktionsname zu einer Lüge geworden, genauso wie Kommentare zu Lügen werden - der Inhalt wird aktualisiert, der Name jedoch nicht. Dies ist kein Streik gegen die Kapselung im Allgemeinen, aber diese besondere Redewendung ist symptomatisch für das große moderne Problem der "Enterprise" -Softwareentwicklung - Schichten und Schichten der Abstraktion und des Leims, die die relevante Logik begraben.
Quirk
@quirk Ich glaube, du stimmst meinem Gesamtpunkt zu? Und mit dem Kleber gehen Sie einem ganz anderen Problem nach. Ich verwende eigentlich Codemaps, wenn ich mir einen neuen Teamcode ansehe. Es ist beängstigend schlecht, was ich für einige große Methoden gesehen habe, die eine Reihe großer Methoden aufrufen, sogar auf der Ebene des MVC-Musters.
AthomSfere
6

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:

boolean isEmailValid = (contact.email != null && contact.emails.contains('@');

if (isEmailValid) {
...

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.

Rich Smith
quelle
Einige sind besorgt über die zusätzliche Codezeile in der Nähe der Bedingung (das bin ich überhaupt nicht), aber sprechen Sie dies vielleicht in Ihrer Antwort an.
Peter Mortensen
5

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:

  • Hast du zu viel Code?
  • Hast du zu wenig Code?

Die wichtigen Fragen sind:

  • Ist die Anwendung insgesamt richtig gestaltet?
  • Ist der Code korrekt implementiert?
  • Ist der Code wartbar?
  • Ist der Code testbar?
  • Ist der Code ausreichend getestet?

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.

Clement Cherlin
quelle
3

Auf einer Ebene haben sie Recht - weniger Code ist besser. Eine andere Antwort zitiert Gate, ich bevorzuge:

„Wenn beim Debuggen Softwarefehler beseitigt werden, müssen sie beim Programmieren eingefügt werden.“ - Edsger Dijkstra

„Beim Debuggen fügen Anfänger Korrekturcode ein. Experten entfernen fehlerhaften Code. ”- Richard Pattis

Die billigsten, schnellsten und zuverlässigsten Komponenten sind diejenigen, die es nicht gibt. - Gordon Bell

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.

Baldrickk
quelle
2

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()und toString()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.)

Scherze
quelle
1

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 Klasse Contactkeine 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 Contactund EmailKlasse 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.

private void LargeMethod() {
    //A lot of code which modifies a lot of values. You do all sorts of tricks here.
    //Code.
    //Code..
    //Code...

    //Email validation code becoming very difficult to test as it will be difficult to ensure 
    //that you have the right data till you reach here in the method
    ValidateEmail();

    //Another whole lot of code that modifies all sorts of values.
    //Extra work to preserve the result of ValidateEmail() for your asserts later.
}

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.

Anzeigename
quelle
1

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.

JimmyJames
quelle
1

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 dictPython 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,

  1. Refaktorieren Sie einen Check in eine Funktion / Methode, wenn Sie ihn kopieren und einfügen möchten. Es gibt zwar gelegentlich gültige Gründe zum Kopieren und Einfügen, aber Sie sollten sich immer schmutzig fühlen. Echte Autoren bringen Sie nicht dazu, einen großen, langen Satz 50 Mal in der gesamten Erzählung nachzulesen, es sei denn, sie versuchen wirklich, ein Thema hervorzuheben.
  2. Eine Funktion / Methode sollte idealerweise ein "Absatz" sein. Die meisten Funktionen sollten ungefähr eine halbe Seite lang sein oder 1 bis 15 Codezeilen, und möglicherweise dürfen nur 10% Ihrer Funktionen eineinhalb Seiten, 45 Zeilen oder mehr umfassen. Sobald Sie über 120 Code- und Kommentarzeilen erreicht haben, muss das Ding in Teile zerlegt werden.
  3. Eine Datei sollte idealerweise ein "Kapitel" sein. Die meisten Dateien sollten höchstens 12 Seiten lang sein, also 360 Code- und Kommentarzeilen. Nur 10% Ihrer Dateien sollten eine Länge von 50 Seiten oder 1500 Code- und Kommentarzeilen haben.
  4. Im Idealfall sollte der größte Teil Ihres Codes mit der Grundlinie der Funktion oder einer Ebene tief eingerückt sein. Basierend auf einigen heuristischen Überlegungen zum Linux-Quelltextbaum sollten, wenn Sie religiös sind, nur 10% Ihres Codes innerhalb der Grundlinie um mindestens 2 Ebenen eingerückt werden, weniger als 5% um mindestens 3 Ebenen. Dies bedeutet insbesondere, dass Dinge, die ein anderes Anliegen "einschließen" müssen, wie die Fehlerbehandlung in einem großen Versuch / Fang, aus der eigentlichen Logik herausgezogen werden sollten.

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.

CR Drost
quelle