Der Grundgedanke, dass da Prinzipien dahinter stehen, die über den Code als syntaktisches Konstrukt hinausgehen, ist dabei wohl der entscheidende. Darauf bezog sich genau der Satz, dass man nicht unreflektiert umsetzen sollte, was man liest.
Als suggestives (fast schon polemisch) wirkendes Beispiel: Jemand liest Extract Function und verwendet das als Argument für bestimmte Refactorings („Fowler hat gesagt, das soll man so machen!!!“). Bis Inline Function kam er halt nicht.
Allgemeiner könnte man wohl sagen, dass Clean Code eben keine „Regeln, sind wie code zu sein hat“, sondern eher die Umsetzung von strukturellen und konzeptuellen Ideen im Code sind. Man kann sich da teilweise bewußt und absichtlich dran halten, und irgendwann „gewöhnt man sich bestimmte Sachen an“.
Auch wenn man glaubt sich etwas schon „angewöhnt“ zu haben, kommt es immer wieder mal vor, dass man beim kritischen Hinterfragen des eigenen Codes merkt, dass man „eine der Regeln verletzt“ hat. In diesem Sinne ist das Wichtigste vielleicht nicht das Einhalten der Regeln an sich, sondern die transzendent-übergeordnete Abstraktion davon: Das kritische Hinterfragen des eigenen Codes.
(BTW: Vieles ^ davon sagt Bob auch selbst auf seinen Seiten in ähnlicher Form).
Ein Beispiel für dieses „verinnerlichen“: SOLID - Wikipedia . Manche Leute kennen das Akronym von irgendeiner Power-Point-Folie im ersten Semester. Andere sind stolz darauf, dass sie nachplappern können, was jeder einzelne Buchstabe bedeutet. In der Detailstufe, wie sie auf Wikipedia zusammengefasst sind, werden die meisten Leute sagen: „Klar!!!111 Mach’ ich!!! Immer!!! Ist doch logisch!!!“. Sich die (ganz unten auf der Wikipedia-Seite verlinkten) einzel-PDFs dazu durchzulesen ist aber nochmal ein Schritt mehr, und ich vermute, dass die wenigsten diesen Schritt gegangen sind.
Fast schon „OT“, weil ein sehr spezielles Beispiel:
Aber der zweite Punkt, denn ich nicht so richtig ausdrücken kann, ist die “Immobilität”, die eingeschränkte Anwendbarkeit. Die if-Kaskade fühlt sich für mich wie “festgeklebt” an.
Eine ähnliche Daumenregel, wie die Clean-Code-Prinzipien, ist: Immer, wenn man ein if
schreibt, sollte man überlegen, ob man das nicht auch anders lösen könnte. Im speziellen kann man erstaunlich viele if
-Abfragen durch geschickte Polymorphie vermeiden (und das nicht nur, um ums Verrecken das if
zu vermeiden, oder unbedingt dieses coole „Polimorfie“ verwenden, von dem man im 2. Semester gehört hat, sondern weil es sinnvoll, flexibler und allgemein besser ist).
Das Beispiel (und die Lösung mit der TreeMap
(die (Clean Code ;-)) auch nur als eine NavigableMap
deklariert sein könnte)) erinnerte mich 1:1 an den Kommentar, den ich unten bei image processing - Java colour detection - Stack Overflow eingestreut hatte - und dort ist auch ein gewisser Trade-Off erwähnt. Manchmal ist ein if
auch OK - wobei gerade die Kommentare dort (z.B. die Frage nach dem „Orange“) deine Argumente hinsichtlich der Flexibilität unterstützen.
Das wichtig(er)e in solchen Fällen finde ich aber allgemein weniger die Frage, wie die einzelnen Codezeilen aussehen. Dem übergeordnet ist für mich die allgegenwärtige Frage nach der Schnittstelle: Wer will diesen Code-Teil wie verwenden? Natürlich war dein Lösungsvorschlag auch nur zur Verdeutlichung. Bei sowas würde man ja ohnehin zumindest eine Methode
private static String categorizeBmi(double bmi) { ... }
schreiben. Die Frage, wer in so einem Fall definiert, wie die Kategorien sind, ist wohl wichtig, um zu entscheiden, ob man dort den if
- oder den NavigableMap
-Ansatz verwendet.
Danach kann man die “Fallunterscheidung” als Datenstruktur weitergeben,
Die Fallunterscheidung weiterzugeben ist in beiden Fällen möglich - wenn man für die Empängerseite die Schnittstelle passend definiert hat:
void printBmiStuff(DoubleFunction<String> bmiCategorizer) {
double bmi = compute();
System.out.println(bmiCategorizer.apply(bmi));
}
void callIt() {
// Entweder so...
TreeMap<Double, String> treeMap = create();
printBmiStuff(treeMap::get);
// Oder so:
printBmiStuff(ThisClass::categorizeBmi);
}
(Modulo double
vs. Double
… tja…).
Das ist dem Emfpänger ja dann egal.
Sofern der „Empfänger“ nicht ein Unit-Test, bei dem deine Map
-Lösung dann bei assertNonNull(bmiCategorizer.apply(-1.23))
aussteigt