Clean Code Kochbuch - Maximiliano Contieri - E-Book

Clean Code Kochbuch E-Book

Maximiliano Contieri

0,0

Beschreibung

Code Smells erkennen und mithilfe inspirierender Rezepte beseitigen - Ideale Ergänzung zu Robert C. Martins Klassiker »Clean Code« - Die eigenen Programmierfähigkeiten verbessern - durch eine Vielzahl inspirierender Rezepte - Beispiele in gängigen Programmiersprachen wie JavaScript, PHP, Java, Python u.v.a. Bis hin zu komplexen Problemen und anspruchsvollen Refactoring-Rezepten. Entwickler, Software Engineers und -Architektinnen, die mit einer umfangreichen Codebasis arbeiten, müssen ihren Code effektiv pflegen und erweitern. In diesem Kochbuch vermittelt Maximiliano Contieri das Konzept des Clean Code, geht aber noch einen Schritt weiter. Er zeigt, wie Sie Verbesserungspotenziale identifizieren und deren Auswirkungen auf den Produktionscode beurteilen. Mithilfe der vorgestellten Techniken kann die Zuverlässigkeit und Entwicklungsfähigkeit der Systeme deutlich verbessert werden. Anhand von Beispielen in JavaScript, PHP, Python, Java und vielen anderen Programmiersprachen bietet dieses Kochbuch bewährte Rezepte, die Sie bei der Erweiterung und Wartung größerer Systeme unterstützen. Contieri beschreibt grundlegende Konzepte wie Lesbarkeit, Kopplung, Testbarkeit, Sicherheit und Erweiterbarkeit sowie Code Smells und Rezepte zu deren Beseitigung. Im Verlauf dieses Buches werden die Refactoring-Rezepte und die Probleme, die sie lösen, immer komplexer. Sie werden: - die Vorteile von Clean Code verstehen und lernen, wie Sie Verbesserungsmöglichkeiten identifizieren - Refactoring-Techniken Schritt für Schritt erlernen - sich mit der Theorie hinter Clean Code vertraut machen - Beispiele aus der Praxis in verschiedenen modernen Programmiersprachen untersuchen - in einem Katalog mit Code Smells stöbern, deren Auswirkungen und Lösungsansätze zu ihrer Beseitigung nachvollziehen - Code schreiben, der auf den Punkt kommt, leichter zu lesen und zu verstehen ist

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 460

Veröffentlichungsjahr: 2024

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Copyright und Urheberrechte:Die durch die dpunkt.verlag GmbH vertriebenen digitalen Inhalte sind urheberrechtlich geschützt. Der Nutzer verpflichtet sich, die Urheberrechte anzuerkennen und einzuhalten. Es werden keine Urheber-, Nutzungs- und sonstigen Schutzrechte an den Inhalten auf den Nutzer übertragen. Der Nutzer ist nur berechtigt, den abgerufenen Inhalt zu eigenen Zwecken zu nutzen. Er ist nicht berechtigt, den Inhalt im Internet, in Intranets, in Extranets oder sonst wie Dritten zur Verwertung zur Verfügung zu stellen. Eine öffentliche Wiedergabe oder sonstige Weiterveröffentlichung und eine gewerbliche Vervielfältigung der Inhalte wird ausdrücklich ausgeschlossen. Der Nutzer darf Urheberrechtsvermerke, Markenzeichen und andere Rechtsvorbehalte im abgerufenen Inhalt nicht entfernen.

Clean Code Kochbuch

Rezepte für gutes Code-Designund bessere Softwarequalität

Maximiliano Contieri

Deutsche Übersetzung vonJens Olaf Koch

Maximiliano Contieri

Lektorat: Ariane Hesse

Übersetzung: Jens Olaf Koch

Korrektorat: Claudia Lötschert, www.richtiger-text.de

Satz: III-satz, www.drei-satz.de

Herstellung: Stefanie Weidner

Umschlaggestaltung: Karen Montgomery, Michael Oréal, www.oreal.de

Bibliografische Information der Deutschen Nationalbibliothek

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

ISBN:

Print978-3-96009-243-8

PDF978-3-96010-862-7

ePub978-3-96010-863-4

1. Auflage 2024

Translation Copyright © 2024 dpunkt.verlag GmbH

Wieblinger Weg 17

69123 Heidelberg

Authorized German translation of the English edition of Clean Code Cookbook ISBN 9781098144722

© 2023 Maximiliano Contieri. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same.

Dieses Buch erscheint in Kooperation mit O’Reilly Media, Inc. unter dem Imprint »O’REILLY«.

O’REILLY ist ein Markenzeichen und eine eingetragene Marke von O’Reilly Media, Inc. und wird mit Einwilligung des Eigentümers verwendet.

Schreiben Sie uns:

Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: [email protected].

Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.

Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.

Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Übersetzer noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.

Inhalt

Geleitwort

Vorwort

1Clean Code

Was ist ein Code-Smell?

Was ist Refactoring?

Was ist ein Rezept?

Warum Clean Code?

Lesbarkeit, Performance – oder beides?

Arten von Software

Maschinengenerierter Code

Hinweise zu verwendeten Begriffen

Entwurfsmuster

Paradigmen der Programmiersprachen

Objekte versus Klassen

Veränderbarkeit

2Festlegung der Axiome

Einführung

Warum ist es ein Modell?

Warum ist es abstrakt?

Warum ist es programmierbar?

Warum ist es partiell?

Warum ist es erklärend?

Wieso geht es um Realität?

Ableitung der Regeln

Das einzig wahre Entwurfsprinzip für Software

3Anämische Modelle

Einführung

Anämische Objekte in Rich Objects konvertieren

Das Wesentliche Ihrer Objekte erkennen

Objekte von Settern befreien

Auf Generatoren verzichten, die anämischen Code produzieren

Automatische Eigenschaften entfernen

DTOs entfernen

Leere Konstruktoren vervollständigen

Getter entfernen

Objektorgie verhindern

Dynamische Eigenschaften entfernen

4Primitive Obsession

Einführung

Small Objects erstellen

Primitive Datentypen in Objekte umwandeln

Assoziative Arrays in Objekte umwandeln

Keine Strings missbrauchen

Zeitstempel in Sequenzierung umwandeln

Teilmengen als Objekte modellieren

String-Validierungen in Objekte umwandeln

Unnötige Eigenschaften entfernen

Datumsintervalle berechnen

5Mutabilität

Einführung

Variablen von var in const ändern

Variablen als veränderlich deklarieren

Veränderungen der Objektessenz verbieten

Veränderliche const-Arrays vermeiden

Lazy Initialization entfernen

Veränderliche Konstanten einfrieren

Seiteneffekte beseitigen

Hoisting verhindern

6Deklarativer Code

Einführung

Geltungsbereich wiederverwendeter Variablen begrenzen

Code durch Aufteilung in Funktionen strukturieren

Versionierte Methoden entfernen

Doppelte Verneinungen entfernen

Falsch zugeordnete Verantwortlichkeiten verschieben

Explizite Iterationen ersetzen

Entwurfsentscheidungen dokumentieren

Magische Zahlen durch Konstanten ersetzen

»Was« und »Wie« trennen

Reguläre Ausdrücke dokumentieren

Yoda-Conditions neu formulieren

Scherzhaft benannte Methoden umbenennen

Callback Hell vermeiden

Gute Fehlermeldungen formulieren

Magische Korrekturen vermeiden

7Namensgebung

Einführung

Abkürzungen ausschreiben

Hilfsfunktionen und -klassen umbenennen und aufteilen

myObjects umbenennen

Ergebnisvariablen umbenennen

Variablen umbenennen, die nach Typen benannt sind

Zu lange Namen kürzen

Abstrakte Namen ändern

Rechtschreibfehler korrigieren

Klassennamen aus Attributen entfernen

Vorangestellte Buchstaben aus Namen von Klassen und Interfaces entfernen

Basic-/Do-Funktionen umbenennen

Mit Pluralformen benannte Klassen auf Singularform ändern

»Collection« aus Namen entfernen

Präfix/Suffix »Impl« aus Klassennamen entfernen

Argumente je nach Rolle bzw. Aufgabe benennen

Redundante Parameternamen ändern

Überflüssigen Kontext aus Namen entfernen

Benennung als »data« vermeiden

8Kommentare

Einführung

Kommentierten Code entfernen

Veraltete Kommentare entfernen

Temporäre logische Steuerungsanweisungen entfernen

Getter-Kommentare entfernen

Kommentare in Funktionsnamen umwandeln

Kommentare innerhalb von Methoden entfernen

Kommentare durch Tests ersetzen

9Standards

Einführung

Codestandards befolgen

Einrückungen standardisieren

Schreibweisen vereinheitlichen

Code auf Englisch schreiben

Reihenfolge von Parametern vereinheitlichen

Kleine Mängel beheben

10Komplexität

Einführung

Wiederholten Code entfernen

Einstellungen/Konfigurationen und Funktionsumschalter entfernen

Zustand über Eigenschaften ändern

Übertriebene Raffinesse aus dem Code entfernen

Mehrere Promises parallel auflösen

Lange Kollaborationsketten auflösen

Methode in ein Objekt extrahieren

Array-Konstruktoren überprüfen

Poltergeist-Objekte entfernen

11Aufgeblähter Code

Einführung

Überlange Methoden unterteilen

Überflüssige Argumente reduzieren

Überflüssige Variablen reduzieren

Überflüssige Klammern entfernen

Überflüssige Methoden entfernen

Überzählige Attribute gruppieren

Importlisten kürzen

Funktionen mit mehreren Aufgaben aufteilen

Überladene Interfaces verschlanken

12YAGNI-Prinzip

Einführung

Toten Code entfernen

Code anstelle von Diagrammen verwenden

Refactoring von Klassen mit nur einer Unterklasse

Interfaces entfernen, die nur an einer Stelle genutzt werden

Missbräuchlich verwendete Entwurfsmuster entfernen

Geschäftsspezifische Collections ersetzen

13Fail-Fast-Prinzip

Einführung

Neuzuweisung von Variablen refaktorieren

Vorbedingungen durchsetzen

Striktere Parameter verwenden

Standardfall bei Switch-Anweisungen entfernen

Beim Iterieren über Collections Änderungen vermeiden

Hash und Gleichheit neu definieren

Refactoring von funktionalen Änderungen trennen

14If-Anweisungen

Einführung

Akzidentelle If-Anweisungen durch Polymorphie ersetzen

Flag-Variablen für Ereignisse deklarativ umbenennen

Boolesche Variablen reifizieren

Switch-/Case-/Elseif-Anweisungen ersetzen

Hartcodierte Bedingungen durch Collections ersetzen

Boolesche Bedingungen in Kurzschluss-Auswertungen umwandeln

Implizites Else in explizites If umwandeln

Verschachtelte Bedingungen refaktorieren

Short-Circuit-Hacks vermeiden

Verschachtelten Pfeilcode refaktorieren

Rückgabe boolescher Werte für Bedingungsprüfungen vermeiden

Vergleiche mit booleschen Werten ändern

Ternäre Ausdrücke vereinfachen

Nicht-polymorphe Funktionen in polymorphe umwandeln

Gleichheitsvergleich ändern

Hartcodierte Geschäftsbedingungen reifizieren

Überflüssige boolesche Operatoren entfernen

Verschachtelte ternäre Ausdrücke refaktorieren

15Nullwerte

Einführung

Nullobjekte erstellen

Optionale Verkettungen entfernen

Optionale Attribute in eine Collection umwandeln

Reale Objekte für Nullwerte verwenden

Darstellung unbekannter Orte ohne Verwendung von null

16Vorzeitige Optimierung

Einführung

IDs für Objekte vermeiden

Vorzeitige Optimierungen entfernen

Bitweise vorzeitige Optimierungen entfernen

Übergeneralisierung reduzieren

Strukturelle Optimierungen ändern

»Boat Anchors« beseitigen

Caches aus Domänenobjekten extrahieren

Auf der Implementierung basierende Callback-Events entfernen

Abfragen aus Konstruktoren entfernen

Code aus Destruktoren entfernen

17Kopplung

Einführung

Versteckte Annahmen explizit machen

Singletons ersetzen

God Objects aufspalten

Klassen bei divergenten Änderungen teilen

Spezielle als Flags genutzte Werte (wie 9999) in normale Werte umwandeln

Shotgun Surgery vermeiden

Optionale Argumente entfernen

Feature Envy vorbeugen

Vermittlerobjekte entfernen

Standardargumente ans Ende verschieben

Ripple-Effekt vermeiden

Zufällige Methoden aus Geschäftsobjekten entfernen

Geschäftslogik aus der Benutzeroberfläche entfernen

Kopplung an Klassen verringern

Datenklumpen beseitigen

Unangemessene Intimität auflösen

Fungible Objekte konvertieren

18Globals

Einführung

Globale Funktionen reifizieren

Statische Funktionen reifizieren

Goto-Anweisungen durch strukturierten Code ersetzen

Globale Klassen entfernen

Globale Datumserstellung anpassen

19Hierarchien

Einführung

Tiefe Vererbungshierarchien verflachen

Jo-Jo-Hierarchien durchbrechen

Subklassifizierung zur Wiederverwendung von Code auflösen

»Ist-ein«-Beziehung durch Verhalten ersetzen

Verschachtelte Klassen entfernen

Isolierte Klassen umbenennen

Konkrete Klassen als final deklarieren

Klassenvererbung explizit definieren

Klassen ohne Verhalten entfernen

Keine vorzeitige Klassenbildung vornehmen

Geschützte Attribute entfernen

Leere Implementierungen vervollständigen

20Testen

Einführung

Private Methoden testen

Beschreibungen zu Assertions hinzufügen

assertTrue in spezifischere Assertions konvertieren

Mock-Objekte durch echte Objekte ersetzen

Generische Assertions verfeinern

Unzuverlässige Tests entfernen

Vergleiche von Gleitkommazahlen vermeiden

Realistische Daten statt Testdaten verwenden

Verletzung der Kapselung vermeiden

Irrelevante Testinformationen entfernen

Keine Pull Requests ohne Testabdeckung zulassen

Tests umformulieren, die von Datumswerten abhängen

Eine neue Programmiersprache lernen

21Technische Schulden

Einführung

Produktionsabhängigen Code entfernen

Fehlertracker entfernen

Warnungen/Strict-Modus nicht ausschalten

ToDos und FixMes verhindern und entfernen

22Ausnahmen

Einführung

Leere Ausnahmeblöcke entfernen

Unnötige Ausnahmen entfernen

Keine Ausnahmen bei erwarteten Fällen auslösen

Verschachtelte Try/Catch-Blöcke vereinfachen

Rückgabecodes durch Ausnahmen ersetzen

Verschachtelten Pfeilcode refaktorieren

Low-Level-Fehler vor Endbenutzern verstecken

Try-Blöcke kurz halten

23Metaprogrammierung

Einführung

Metaprogrammierung entfernen

Anonyme Funktionen reifizieren

Auf Präprozessoren verzichten

Dynamische Methoden entfernen

24Datentypen

Einführung

Typprüfungen entfernen

Mit truthy-Werten umgehen

Gleitkommazahlen in Dezimalzahlen konvertieren

25Sicherheit

Einführung

Benutzereingaben bereinigen

Sequenzielle IDs ändern

Paketabhängigkeiten entfernen

Problematische reguläre Ausdrücke ersetzen

Sichere Deserialisierung von Objekten

Glossar

Index

Geleitwort

»Software is eating the world.« Diese Aussage von Marc Andreessen ist eines meiner Lieblingszitate, fast schon ein Meme, das den gegenwärtigen Stand der Dinge beschreibt. Nie zuvor in der Geschichte der Menschheit gab es so viel Software wie heute, und noch nie zuvor war sie für so viele Aspekte des Lebens zuständig. Wer in einem urbanen Umfeld lebt, ist fast nur noch von Dingen umgeben, die durch Software gesteuert werden. Jedes Jahr übertragen wir mehr und mehr Kontrolle an Software. Und durch den explosiven und disruptiven Durchbruch der künstlichen Intelligenz wird dieser Trend nur noch verstärkt: Die KIs, mit denen wir interagieren, sind ebenfalls Software.

Ich gehöre zu einer Generation, in der es normal war, zuerst Programmierer zu werden, bevor man zu einem Anwender von Software wurde. Mit 16 fing ich an, meine ersten kleinen Programme zu schreiben. Mit 18 begann ich, an umfangreicheren Systemen zu arbeiten, und sah mich mit der grundlegenden Tatsache konfrontiert, die auch den Autor zu diesem willkommenen und notwendigen Buch motiviert hat: dass die Software, von der immer mehr abhängt, von Menschen geschrieben wird, als Code. Und die Qualität dieses Codes beeinflusst direkt die Qualität der Software, ihre Wartbarkeit, Lebensdauer, ihre Kosten und Leistung.

Bei der Arbeit an diesen frühen Systemen, die von sehr kleinen Teams von zwei oder drei Personen programmiert wurden, lernte ich auf die harte Tour, warum es so wichtig ist, Clean Code – sauberen Code – zu schreiben. Hätte ich damals ein Buch wie dieses gehabt, hätte ich viel Zeit sparen können.

Das bedeutet nicht, dass dieses Buch nur in den prähistorischen Zeiten der Informatik relevant gewesen wäre oder dass seine Zielgruppe aus Programmieranfängern besteht, denen die Grundlagen beigebracht werden müssen. Ganz im Gegenteil.

In diesem Buch finden Sie – ganz im Stil eines Kochbuchs – zahlreiche einfache Rezepte, um eine Vielzahl von Fallstricken und häufigen Fehlerquellen beim Programmieren zu vermeiden. Oft ist nicht das Rezept der wertvollste Teil, sondern die Tatsache, dass ein bestimmtes Thema angesprochen und diskutiert wird. Das gibt uns eine Grundlage, um darüber nachzudenken, wie wir dieses Problem in unserem Code lösen und die Sauberkeit – die »Cleanliness« – unserer Lösung bewerten können. Maximiliano Contieris Schreibstil ist klar und direkt, genau wie wir uns unseren Code wünschen. Jedes Rezept enthält Codebeispiele, um mögliche Zweifel an dessen korrekter Anwendung zu beseitigen.

Es scheint, als ob Sauberkeit und Klarheit des Codes nur Sache der Programmierer wären. Tatsächlich beginnen die Probleme aber schon lange vorher, in der Entwurfsphase, und erstrecken sich bis zur Bereitstellung von Ressourcen, den Entwicklungsrichtlinien, dem Projekt- und Teammanagement, den Wartungs- und Entwicklungsphasen. Ich glaube, dass fast alle in der Softwarebranche von diesem Buch profitieren können, weil es eine Vielzahl von häufigen Problemen im Code – also in dem »Material«, aus dem Software gemacht wird: die Software, die die Welt »auffrisst« – sehr gut veranschaulicht und erklärt.

Man könnte denken, das Schreiben von Code sei überholt und KI und große Sprachmodelle würden das Programmieren bald ohne jegliches menschliches Zutun übernehmen. Den Beispielen zufolge, die mir täglich begegnen, sieht die Realität noch anders aus. Das Ausmaß der Halluzinationen (grundlegende Fehler, Interpretationsprobleme, Schwachstellen und Wartungsprobleme), die der von KI geschriebene Code aufweist, verhindern das. Wir befinden uns jedoch ganz eindeutig in einer Übergangsphase, in der »technologische Zentauren« von großer Bedeutung sein werden, also erfahrene Programmierer, die den von automatisierten Systemen produzierten Code überwachen, korrigieren und verbessern. Und solange Menschen Code lesen und pflegen müssen, ist Clean Code, so wie er in diesem Buch vermittelt wird, essenziell.

– Carlos E. Ferro

Lic. en Ciencias de la computación

Senior Software Engineer bei Quorum Software

Buenos Aires

20. Juni 2023

Vorwort

Code ist überall zu finden, von der Webentwicklung bis hin zu Smart Contracts, in eingebetteten Systemen, Blockchains, der Steuerungssoftware des James-Webb-Weltraumteleskops, chirurgischen Robotern und in vielen anderen Anwendungen. Software erobert tatsächlich die Welt, und wir erleben derzeit den Aufstieg professioneller KI-Tools zur Codeerzeugung. Das macht Clean Code wichtiger denn je. Da wir mit immer größeren proprietären oder Open-Source-Codebasen arbeiten, ist Clean Code der Schlüssel, um die Codebasis frisch und entwicklungsfähig zu halten.

Für wen dieses Buch gedacht ist

Dieses Buch hilft Ihnen, häufig auftretende Probleme im Code zu identifizieren, erklärt die Konsequenzen dieser Probleme und bietet einfache, nachvollziehbare Rezepte, um sie zu vermeiden. Es unterstützt Programmierer, Code-Reviewer, Softwarearchitekten und Studierende dabei, ihre Programmierfähigkeiten zu verbessern und damit auch die bereits bestehende Software.

Wie dieses Buch aufgebaut ist

Dieses Buch umfasst 25 Kapitel. Jedes Kapitel beginnt mit einigen Prinzipien und Grundlagen, die die Vorteile von Clean Code, seine Konsequenzen und die Nachteile aufzeigen, die bei falscher Anwendung entstehen. Das erste Kapitel behandelt die wesentliche Grundregel für Clean Code: Bilden Sie in Ihrem Design reale Entitäten 1:1 ab. Diese Regel dient als Basis, von der aus alle anderen Prinzipien abgeleitet werden können.

In jedem Kapitel finden Sie mehrere thematisch gruppierte Rezepte, die Werkzeuge und Tipps zur Änderung Ihres Codes enthalten. Alle Rezepten zielen darauf ab, positive Veränderungen und Verbesserungen an Ihrem aktuellen Code vorzunehmen. Neben den Rezepten und Beispielen werden verschiedene Designprinzipien, Heuristiken und Regeln vorgestellt. Die Rezepte enthalten Codebeispiele in verschiedenen Programmiersprachen, denn Clean Code ist keine Eigenschaft, die sich einer bestimmten Programmiersprache zuordnen lässt. Viele Bücher über Refactoring konzentrieren sich auf eine einzige Sprache, und bei aktualisierten Neuausgaben beschäftigen sich die Autoren gerne mit der jeweils neuesten, angesagten Programmiersprache. Dieses Buch ist dagegen sprachagnostisch angelegt, und die meisten Rezepte gelten universell (es sei denn, es ist wird ausdrücklich etwas anderes angegeben).

Lesen Sie den Code als Pseudocode, auch wenn er größtenteils in der dargestellten Form lauffähig ist. Wenn ich mich zwischen Lesbarkeit und Performance entscheiden muss, wähle ich stets die Lesbarkeit. Im Laufe des Buchs definiere ich gängige Begriffe, aber Sie finden sie auch gesammelt im »Glossar«.

Was Sie zur Verwendung dieses Buchs benötigen

Um die Codebeispiele auszuführen, benötigen Sie eine Arbeitsumgebung wie die O’Reilly-Sandboxes (https://learning.oreilly.com/interactive) oder Replit (https://replit.com). Ich möchte Sie ermutigen, die Codebeispiele in Ihre bevorzugte Programmiersprache zu übersetzen. Das lässt sich mit KI-Codegeneratoren heutzutage kostenlos erledigen. Um die Codebeispiele in diesem Buch zu erstellen, habe ich Tools wie GitHub Copilot, OpenAI Codex, Gemini, ChatGPT und viele andere verwendet. Dadurch konnte ich in diesem Buch mehr als 25 verschiedene Sprachen verwenden, auch wenn ich in vielen davon kein Experte bin.

Zugang zur Digitalversion dieses Buchs

Eine kostenlose englischsprachige Version als stets verfügbare, durchsuchbare Onlineausgabe finden Sie unter https://www.cleancodecookbook.com.

Konventionen, die in diesem Buch verwendet werden

In diesem Buch werden die folgenden typografischen Konventionen verwendet:

Kursiv

Zeigt neue Begriffe, URLs, E-Mail-Adressen, Dateinamen und Dateierweiterungen an.

Nichtproportionalschrift

Wird für Programmlistings verwendet, aber auch innerhalb von Absätzen, um dort auf Programmelemente wie Variablen- oder Funktionsnamen, Datenbanken, Datentypen, Umgebungsvariablen, Anweisungen und Schlüsselwörter zu verweisen.

Fette Nichtproportionalschrift

Zeigt Befehle oder anderen Text an, der vom Benutzer wortgetreu eingegeben werden muss.

Kursive Nichtproportionalschrift

Zeigt Text an, der durch Benutzereingaben oder durch kontextabhängige Werte ersetzt werden soll.

Dieses Element weist auf einen Tipp oder Vorschlag hin.

Dieses Element kennzeichnet einen allgemeinen Hinweis.

Dieses Element weist auf eine Warnung hin.

Verwendung von Codebeispielen

Zusätzliches Material wie Codebeispiele und Übungen finden Sie zum Download unter: https://github.com/mcsee/clean-code-cookbook.

Bei technischen Fragen oder Problemen mit den Codebeispielen wenden Sie sich bitte per E-Mail an [email protected].

Dieses Buch soll Ihnen bei Ihren Aufgaben helfen. Sie dürfen den in diesem Buch bereitgestellten Beispielcode in Ihren Programmen und Ihrer Dokumentation verwenden, ohne uns um Erlaubnis bitten zu müssen, außer Sie reproduzieren einen wesentlichen Teil des Codes. Wenn Sie beispielsweis ein Programm schreiben, das mehrere Teile des Codes aus diesem Buch nutzt, benötigen Sie keine Genehmigung. Der Verkauf oder die Verbreitung von Beispielen aus O’Reilly-Büchern erfordert dagegen eine Genehmigung. Die Beantwortung einer Frage durch das Zitieren dieses Buchs und von Beispielcode ist nicht genehmigungspflichtig. Das Einbinden einer erheblichen Menge an Beispielcode aus diesem Buch in die Dokumentation Ihres Produkts erfordert dagegen eine Genehmigung.

Wir freuen uns über eine Quellenangabe, verlangen sie aber im Allgemeinen nicht. Eine Quellenangabe sollte in der Regel den Titel, den Autor, den Verlag und die ISBN umfassen. Zum Beispiel: Clean Code Kochbuch von Maximiliano Contieri (O’Reilly). Copyright 2024 Maximiliano Contieri.

Wenn Sie der Meinung sind, dass Ihre Verwendung von Codebeispielen nicht unter die Fair-Use-Regelung oder die oben genannte Erlaubnis fällt, können Sie uns bitte unter [email protected] kontaktieren.

Danksagungen

Dieses Buch ist meiner Frau Maria Virginia gewidmet, die mich stets liebevoll unterstützt hat, sowie meinen geliebten Töchtern Malena und Miranda und meinen Eltern Juan Carlos und Alicia.

Ich bin Maximo Prieto und Hernan Wilkinson, die wesentlich zu den Ideen beigetragen haben, die in diesem Buch vorgestellt werden, zu großem Dank für ihre wertvollen Einsichten und ihr Fachwissen verpflichtet. Mein Dank gilt auch meinen Kollegen von Ingenieria de Software dafür, dass sie ihre Ideen mit mir geteilt haben, und meinen Kolleginnen und Kollegen an der Ciencias Exactas, der Fakultät für Exakte und Naturwissenschaften der Universidad de Buenos Aires, für ihr geteiltes Wissens und ihrer Erfahrung über viele Jahre.

Abschließend möchte ich den technischen Gutachtern Luben Alexandrov, Daniel Moka und Carlos E. Ferro sowie meiner Lektorin Sara Hunter danken, deren Betreuung und Ratschläge dieses Buch erheblich verbessert haben.

KAPITEL 1

Clean Code

Als Martin Fowler in seinem Buch Refactoring: Improving the Design of Existing Code (dt. Refactoring: Wie Sie das Design bestehender Software verbessern) den Begriff »Refactoring« definierte, beschrieb er dessen Stärken und Vorteile und die Gründe für Refaktorierungen. Glücklicherweise kennen die meisten Entwickler mehr als zwei Jahrzehnten später die Bedeutung von Refactoring und Code-Smells. Entwickler kämpfen täglich mit technischen Schulden, und Refactoring ist zu einem zentralen Bestandteil der Softwareentwicklung geworden. In seinem grundlegenden Buch nutzt Fowler Refactorings, um Code-Smells zu eliminieren. Dieses Buch stellt einige dieser Refactorings in Form semantischer Rezepte vor, mit denen Sie Ihre eigenen Lösungen verbessern können.

Was ist ein Code-Smell?

Ein Code-Smell, wörtlich ein »übler Codegeruch«, signalisiert ein Problem im Code. Oft deuten Menschen das Vorhandensein von Code-Smells als Indiz dafür, dass das gesamte System auseinandergenommen und neu aufgebaut werden muss. Das widerspricht aber dem Geist der ursprünglichen Definition. Code-Smells sind lediglich Indikatoren für mögliche Verbesserungen. Ein Code-Smell verrät nicht direkt, was falsch ist, er mahnt nur zu besonderer Vorsicht.

Die in diesem Buch vorgestellten Rezepte bieten Lösungen für diese Symptome und ihre zugrunde liegenden Probleme. Wie in jedem Kochbuch sind auch diese Rezepte Vorschläge und die Hinweise zu Code-Smells Leitlinien und Heuristiken, keine starren Regeln. Bevor Sie ein Rezept blind anwenden, sollten Sie die zugrunde liegenden Probleme verstehen und die Vor- und Nachteile Ihres Entwurfs und Codes abwägen. Zu einem guten Codedesign gehört es, Leitlinien mit praktischen Überlegungen und dem jeweiligen Kontext in Einklang zu bringen.

Was ist Refactoring?

Fowler liefert in seinem Buch zwei sich ergänzende Definitionen:

Refactoring (Substantiv): eine Änderung der internen Struktur einer Software, um sie verständlicher und kostengünstiger veränderbar zu machen, ohne ihr beobachtbares Verhalten zu ändern.

refaktorieren (Verb): Software durch die Anwendung einer Serie von Refactorings zu restrukturieren, ohne ihr beobachtbares Verhalten zu verändern.

Der Begriff »Refactoring« wurde 1990 von Ralph Johnson und William Opdyke geprägt und im Jahr 1992 zum Thema von Opdykes Dissertation, »Refactoring Object-Oriented Frameworks« (https://oreil.ly/zBCkI). Populär wurden Refactorings später durch Fowlers Buch. Seit Fowlers Definition hat sich viel in diesem Bereich getan. Die meisten integrierten Entwicklungsumgebungen (IDEs) unterstützen heutzutage automatische Refactorings und führen strukturelle Änderungen sicher durch, ohne das Systemverhalten zu verändern. Dieses Buch enthält viele Rezepte für solche automatischen, sicher anwendbaren Refactorings. Es bietet darüber hinaus auch semantische Refactorings an, die allerdings nicht automatisch sicher sind, da sie das Systemverhalten ändern können. Seien Sie also bei der Anwendung von Rezepten mit semantischen Refaktorierungen vorsichtig, da sie Ihre Software in Mitleidenschaft ziehen können. Ich werde in entsprechenden Fällen darauf hinweisen. Wenn das Verhalten Ihres Codes durch Tests umfassend abgedeckt ist, können Sie sicher sein, dass wichtige Geschäftsszenarien nicht beeinträchtigt werden. Refactoring-Rezepte sollten nicht gleichzeitig mit Fehlerkorrekturen oder der Entwicklung neuer Funktionen angewendet werden.

Die meisten modernen Unternehmen nutzen in ihren CI/CD-Pipelines leistungsstarke Testsuiten. Sehen Sie sich auch Software Engineering at Google von Titus Winters et al. (O’Reilly 2020) an, um herauszufinden, ob diese Testabdeckungssuiten bei Ihnen vorhanden sind.

Was ist ein Rezept?

Ich verwende den Begriff »Rezept« in einem eher lockeren Sinne. Ein Rezept besteht aus einer Reihe von Anweisungen, um etwas zu erstellen oder zu verändern. Die Rezepte in diesem Buch funktionieren am besten, wenn Sie die dahinterstehende Absicht erfassen, es aber auf Ihre eigene Art und Weise umsetzen. Andere Kochbücher in dieser Reihe sind konkreter und enthalten Schritt-für-Schritt-Lösungen. Um die Rezepte dieses Buchs zu verwenden, müssen Sie sie in die von Ihnen genutzte Programmiersprache und für Ihr Softwaredesign übersetzen. Die Rezepte sollen Ihnen helfen, ein Problem zu verstehen, die damit verbundenen Konsequenzen zu erkennen und Ihren Code zu verbessern.

Warum Clean Code?

Clean Code – guter, sauber formulierter Code – lässt sich leicht lesen, verstehen und warten. Er ist gut strukturiert, präzise und verwendet aussagekräftige Bezeichnungen für Variablen, Funktionen und Klassen. Er orientiert sich zudem an etablierten Best Practices und Entwurfsmustern, wobei Lesbarkeit und Verhalten Vorrang vor Performance und Implementierungsdetails haben.

Clean Code ist in allen sich weiterentwickelnden Systemen, in denen tägliche Änderungen stattfinden, sehr wichtig. Das gilt ganz besonders für Umgebungen, in denen es nicht möglich ist, beliebig schnell Updates durchzuführen, wie etwa bei Embedded Systems, Raumsonden, Smart Contracts, mobilen Apps (zum Beispiel wegen Verzögerungen durch interne Reviews der Store-Anbieter).

Während sich klassische Refactoring-Bücher, Websites und IDEs auf Refaktorierungen konzentrieren, die das Systemverhalten nicht ändern, enthält dieses Buch sowohl Rezepte für solche Szenarien (z.B. sichere Umbenennungen), aber auch semantische Refactorings, bei denen sich die Art und Weise ändert, in der bestimmte Probleme gelöst werden. Es ist wichtig, den Code, die zugrunde liegenden Probleme und die Intention des Rezepts zu verstehen, um angemessene Anpassungen vornehmen zu können.

Lesbarkeit, Performance – oder beides?

Dieses Buch dreht sich um Clean Code. Einige der Rezepte mögen nicht zu den herausragend performanten Lösungen gehören. Im Zweifel ziehe ich aber die Lesbarkeit der Leistung vor. Deshalb widme ich ein ganzes Kapitel (Kapitel 16) der vorzeitigen Optimierung, bei der Leistungsprobleme adressiert werden, ohne dass für die Notwendigkeit der Optimierung ausreichende Evidenz vorliegt.

Für leistungsabhängige, unternehmenskritische Aufgaben besteht die beste Strategie darin, zunächst Clean Code zu schreiben und diesen mit Tests abzudecken, um anschließend die Engpässe unter Anwendung des Pareto-Prinzips zu verbessern. Dieses Prinzip, angewendet auf Software, besagt, dass man die Performance um 80% steigern kann, wenn man 20% der kritischen Engpässe bearbeitet.

Diese Herangehensweise wirkt der Tendenz entgegen, vorzeitige Optimierungen vorzunehmen, ohne dass dafür ausreichend Evidenz vorliegt. Vorzeitige Optimierungen führen meist nur zu geringen Verbesserungen und können Clean Code beeinträchtigen.

Arten von Software

Die meisten Rezepte in diesem Buch zielen auf Backend-Systeme mit komplexer Geschäftslogik. Der Simulator, dessen Konstruktion wir in Kapitel 2 beginnen werden, eignet sich perfekt dafür. Da die Rezepte domänenunabhängig sind, können Sie die meisten auch bei der Frontend-Entwicklung, für Datenbanken, Embedded Systems, Blockchains und in vielen andere Szenarien anwenden. Es gibt zudem spezifische Rezepte mit Codebeispielen für UX, Frontend, Smart Contracts und andere Bereiche (siehe beispielsweise »Low-Level-Fehler vor Endbenutzern verstecken« auf Seite 362).

Maschinengenerierter Code

Muss man denn auch heute, da es eine Vielzahl von Tools für computergenerierten Code gibt, noch über Clean Code nachdenken? Die Antwort ist ein klares Ja. Sogar mehr denn je. Für die Programmierung gibt es viele kommerzielle Codeassistenten. Allerdings haben sie (noch) nicht die vollständige Kontrolle: Sie agieren als Copiloten und Helfer, aber die Designentscheidungen trifft weiterhin der Mensch.

Zum Zeitpunkt der Erstellung dieses Buchs erzeugen die meisten kommerziellen und KI-Tools anämische Lösungen und Standardalgorithmen. Sie sind jedoch erstaunlich nützlich, wenn einem gerade nicht einfällt, wie man eine kleine, spezifische Funktion erstellt, oder um zwischen Programmiersprachen zu übersetzen. Beim Schreiben dieses Buchs habe ich solche Werkzeuge intensiv genutzt. Ich beherrsche die mehr als 25 Programmiersprachen, die ich in den Rezepten verwendet habe, nicht alle selbst. Viele Codebeispiele habe ich mit der Unterstützung einer Vielzahl von Assistenztools in verschiedene Sprachen übersetzt und getestet. Ich möchte auch Sie dazu einladen, alle verfügbaren Werkzeuge zu nutzen, um die Rezepte dieses Buchs in Ihre bevorzugte Sprache zu übersetzen. Diese KI-Werkzeuge sind gekommen, um zu bleiben, und zukünftige Entwickler werden technologische Zentauren sein: halb Mensch, halb Maschine.

Hinweise zu verwendeten Begriffen

Im gesamten Buch verwende ich die folgenden Begriffe austauschbar:

Methoden/Funktionen/Prozeduren

Attribute/Instanzvariablen/Eigenschaften

Protokoll/Verhalten/Interface/Schnittstelle

Argumente/Kollaborateure/Parameter

Anonyme Funktionen/Closures/Lambdas

Die Unterschiede zwischen diesen Begriffen sind subtil und teilweise sprachabhängig. Sofern notwendig, werde ich zur Klärung einen Hinweis hinzufügen.

Entwurfsmuster

In diesem Buch setze ich voraus, dass Sie als Leser über ein grundlegendes Verständnis objektorientierter Designkonzepte verfügen. Einige Rezepte basieren auf populären Entwurfsmustern, einschließlich jener, die im Buch »Gang of Four« Design Patterns vorgestellt werden. Andere Rezepte nutzen weniger bekannte Muster wie das Nullobjekt oder das Methodenobjekt. Darüber hinaus bietet dieses Buch Erklärungen und Anleitungen zum Ersetzen von Mustern, die heute als Antipatterns angesehen werden, wie beispielsweise das Singleton-Muster in »Singletons ersetzen« auf Seite 265.

Paradigmen der Programmiersprachen

David Farley schreibt:

Die Besessenheit unserer Branche von Sprachen und Tools hat unserem Beruf geschadet. Das bedeutet nicht, dass es keine Fortschritte im Sprachdesign gibt. Doch die meisten Veränderungen in diesem Bereich scheinen sich auf die falschen Dinge zu konzentrieren, wie zum Beispiel syntaktische statt strukturelle Verbesserungen.

Die Clean-Code-Konzepte, die in diesem Buch vorgestellt werden, sind auf eine Vielzahl von Programmierparadigmen anwendbar. Viele dieser Ideen haben ihre Wurzeln in der strukturierten und der funktionalen Programmierung, während andere aus der objektorientierten Welt stammen. Diese Konzepte können Ihnen helfen, im Rahmen jedes Paradigmas eleganteren und effizienteren Code zu schreiben.

Die meisten Rezepte werde ich in objektorientierten Sprachen formulieren und einen Simulator namens MAPPER bauen, bei dem ich Objekte als Metaphern für reale Entitäten verwende. Ich werde im Verlauf des Buchs häufig auf den MAPPER Bezug nehmen. Viele Rezepte werden Sie ermutigen, an verhaltensorientierten und deklarativen Code (siehe Kapitel 6) statt an Implementierungscode zu denken.

Objekte versus Klassen

In den meisten Rezepten dieses Buchs geht es um Objekte und nicht um Klassen – obwohl es ein ganzes Kapitel über Klassenbildung gibt: Kapitel 19. Zum Beispiel lautet ein Rezept »Das Wesentliche Ihrer Objekte erkennen« auf Seite 37 und nicht »Das Wesentliche Ihrer Klassen erkennen«. In diesem Buch geht es um die Verwendung von Objekten zur Abbildung realer Gegenstände.

Sie können diese Objekte auf beliebige Art und Weise erstellen: durch Klassenbildung, Prototyping, Fabriken, Klonen usw. In Kapitel 2 erörtere ich die Bedeutung des Mappings Ihrer Objekte und die Notwendigkeit, Dinge zu modellieren, die Sie in der realen Welt beobachten können. Um Objekte zu erstellen, verwenden viele Sprachen Klassen, die künstliche bzw. abstrakte Konstrukte und in der realen Welt nicht offensichtlich sind. Natürlich benötigen Sie Klassen, wenn Sie eine klassenbasierte Programmiersprache verwenden. Sie stehen aber nicht im Mittelpunkt der Rezepte.

Veränderbarkeit

Bei Clean Code geht es nicht nur darum, dass Ihre Software korrekt arbeitet. Er soll auch gewährleisten, dass sie leicht zu warten und weiterzuentwickeln ist. Dave Farley betont in Modern Software Engineering (dt. Modernes Software Engineering), dass Sie darin versiert sein müssen, kontinuierlich zu lernen und Software so zu gestalten, dass sie flexibel anpassbar ist. Das stellt eine bedeutende Herausforderung für die Technologiebranche dar, und ich hoffe, dass Ihnen dieses Buch dabei helfen kann, mit diesen Entwicklungen Schritt zu halten.

KAPITEL 2

Festlegung der Axiome

Einführung

Eine gängige Definition von Software (https://oreil.ly/MqGxG) lautet:

Die Befehle, die ein Computer ausführt, im Gegensatz zur Hardware, dem physischen Gerät, auf dem diese Befehle laufen.

Software wird hier durch ihr Gegenteil definiert: Sie ist demnach alles, was keine Hardware ist. Das ist keine besonders gute Beschreibung ihres Wesens. Eine weitere populäre Definition (https://oreil.ly/SVbXv) lautet:

Software besteht aus Anweisungen, die einem Computer mitteilen, was er tun soll. Sie umfasst die Gesamtheit der Programme, Verfahren und Routinen, die mit dem Betrieb eines Computersystems zusammenhängen. Der Begriff »Software« dient dazu, diese Anweisungen von der Hardware, d.h. den physischen Komponenten eines Computersystems, zu unterscheiden. Ein Satz solcher Anweisungen, der die Hardware eines Computers anweist, eine Aufgabe auszuführen, wird als Programm oder Softwareprogramm bezeichnet.

Bereits vor vielen Jahrzehnten erkannten Entwickler, dass Software weit mehr ist als nur eine Reihe von Anweisungen. Im Laufe dieses Buchs werden wir uns mit Systemverhalten beschäftigen und erkennen, dass der Hauptzweck von Software darin besteht,

etwas zu simulieren, das in einer möglichen Realität geschieht.

Diese Vorstellung führt uns zurück zu den Ursprüngen moderner Programmiersprachen wie Simula.

Simula

Simula war nicht nur die erste objektorientierte Programmiersprache, sondern auch die erste, die Klassen einführte. Der Name der Sprache deutet klar darauf hin, dass der Zweck der Softwareentwicklung die Schaffung einer Simulation war. Das gilt auch heute noch für die meisten Computerprogramme.

In der Wissenschaft erstellt man Simulationen, um die Vergangenheit zu verstehen und die Zukunft vorherzusagen. Seit Platon bemühen sich die Menschen darum, zutreffende Modelle der Wirklichkeit zu entwickeln. Wir können Software als den Bau eines Simulators definieren, der mit dem Akronym MAPPER umschrieben wird:

Modell: abstrakte, partielle und programmierbare Erklärung der Realität

Dieses Akronym wird uns durch das ganze Buch hindurch begleiten. Schauen wir uns an, was diesen MAPPER ausmacht.

Warum ist es ein Modell?

Ein Modell entsteht, indem man einen bestimmten Aspekt der Realität aus einer speziellen Perspektive, unter Verwendung eines bestimmten Paradigmas, betrachtet. Es verkörpert keine endgültige, unveränderliche Wahrheit, sondern das genaueste Verständnis, das auf der Grundlage des jeweils aktuellen Wissensstands möglich ist. Die Aufgabe eines Softwaremodells, wie die jedes anderen Modells auch, ist die Vorhersage eines Verhaltens in der realen Welt.

Modell

Ein Modell erklärt den Gegenstand, den es beschreibt, durch intuitive Konzepte oder Metaphern. Das Endziel eines Modells ist das Verständnis, wie etwas funktioniert. Peter Naur (https://oreil.ly/6FiD8) zufolge bedeutet Programmieren, Theorien und Modelle zu entwickeln.

Warum ist es abstrakt?

Das Modell ist mehr als die Summe seiner Teile. Man kann es nicht vollständig verstehen, wenn man nur die einzelnen Komponenten betrachtet. Das Modell basiert auf Vereinbarungen und Verhaltensweisen, die nicht notwendigerweise im Detail beschreiben, wie bestimmte Dinge zu tun sind.

Warum ist es programmierbar?

Ihr Modell sollte innerhalb eines Simulators ausgeführt werden, der die gewünschten Bedingungen nachbildet. Das könnte ein Turing-Modell sein (wie heutige kommerzielle Computer), ein Quantencomputer (Computer der Zukunft) oder jeder andere Simulator, der mit der Entwicklung des Modells Schritt halten kann. Sie können das Modell so programmieren, dass es auf Ihre Eingriffe in bestimmter Weise reagiert, und dann beobachten, wie es sich verändert.

Turing-Modell

Ein Computer, der auf dem Turing-Modell basiert, ist eine theoretische Maschine, die jede berechenbare Aufgabe ausführen kann, die durch einen Satz von Anweisungen oder einen Algorithmus beschrieben wird. Die Turing-Maschine gilt als die theoretische Grundlage der modernen Informatik und dient als Modell für den Entwurf und die Analyse von heutigen Computern und Programmiersprachen.

Warum ist es partiell?

Um das Problem zu modellieren, das Sie interessiert, werden Sie nur einen spezifischen Teilaspekt der Realität betrachten. In wissenschaftlichen Modellen ist es üblich, irrelevante Aspekte zu vereinfachen, um ein Problem besser isolieren zu können. Bei der Durchführung wissenschaftlicher Experimente werden nur spezifische, isolierte Variablen kontrolliert verändert, um Hypothesen testen zu können.

In Ihrem Simulator werden Sie nicht die gesamte Realität abbilden können, sondern nur einen wesentlichen Teil davon. Es ist nicht notwendig, den gesamten Beobachtungsgegenstand, also die reale Welt, zu modellieren, sondern nur ein spezifisches Verhalten, das von Interesse ist. Viele Rezepte in diesem Buch adressieren das Problem, dass Modelle unter Overdesign leiden, weil sie zu viele unnötige Details enthalten.

Warum ist es erklärend?

Das Modell sollte hinreichend deklarativ gestaltet sein, um seine Entwicklung beobachten und das Verhalten in der modellierten Realität zu verstehen und dieses vorherzusagen zu können. Es sollte in der Lage sein, zu erklären, was es tut und wie es sich verhält. Viele moderne Algorithmen, die auf maschinellem Lernen beruhen, neigen dazu, als Blackbox zu agieren, und liefern wenig Informationen darüber, wie genau sie ihren Output generieren (und halluzinieren bisweilen). Modelle sollten aber fähig sein, zu erklären, was sie getan haben, auch wenn sie die spezifischen Schritte, die sie dazu unternommen haben, nicht preisgeben.

Erklären

Aristoteles sagte, dass »Erklären bedeutet, die Ursachen zu finden«. Seiner Auffassung zufolge hat jedes Phänomen oder Ereignis eine oder mehrere Ursachen, die es hervorrufen oder bestimmen. Das Ziel der Wissenschaft ist es, die Ursachen von Naturphänomenen zu erkennen und zu verstehen und daraus abzuleiten, wie sich diese in der Zukunft verhalten werden.

Für Aristoteles bestand das »Erklären« darin, all diese Ursachen zu identifizieren und zu verstehen, wie sie miteinander interagieren, um ein bestimmtes Phänomen zu erzeugen. Beim »Vorhersagen« geht es hingegen um die Fähigkeit, dieses Wissen über die Ursachen zu nutzen, um zu prognostizieren, wie sich ein Phänomen in der Zukunft verhalten wird.

Wieso geht es um Realität?

Das Modell muss Bedingungen reproduzieren, die in einer beobachtbaren Umgebung auftreten. Letztlich geht es darum, wie bei jeder Simulation, Verhalten in der realen Welt vorherzusagen. In diesem Buch werden Sie viel über die Realität, die reale Welt und reale Entitäten hören. Die reale Welt wird Ihre ultimative Quelle der Wahrheit sein.

Ableitung der Regeln

Nachdem wir nun verstanden haben, was Software ist, können wir beginnen, gute Modellierungs- und Designpraktiken abzuleiten. Die MAPPER-Prinzipien werden in den Rezepten dieses Buchs regelmäßig erwähnt.

In den folgenden Kapiteln lernen Sie Prinzipien, Heuristiken, Rezepte und Regeln kennen, um hervorragende Softwaremodelle zu erstellen, die auf dem bereits vorgestellten, einfachen Axiom basieren: Modell: abstrakte, partielle und programmierbare Erklärung der Realität. Die Arbeitsdefinition von Software für dieses Buch lautet: »Ein Simulator, der den MAPPER-Prinzipien entspricht.«

Axiom

Ein Axiom ist eine Aussage oder ein Satz, der ohne Beweis als wahr angenommen wird. Es bildet die Grundlage für ein logisches System, das Schlussfolgerungen und Deduktionen ermöglicht. Durch die Festlegung einer Reihe grundlegender Konzepte und Beziehungen können weitere wahre Aussagen abgeleitet werden.

Das einzig wahre Entwurfsprinzip für Software

Wenn wir das gesamte Paradigma des Softwaredesigns auf einer einzigen Regel aufbauen, können wir es einfach halten und gleichzeitig ausgezeichnete Modelle erstellen. Minimalistisch und axiomatisch zu sein, bedeutet, aus einer einzigen Definition einen Satz von Regeln ableiten zu können:

Das Verhalten jedes einzelnen Elements ist insofern Teil der Architektur, als es dabei helfen kann, über das System nachzudenken. Das Verhalten von Elementen umfasst ihre Interaktion untereinander und mit der Umwelt. Das ist eindeutig Teil unserer Architekturdefinition und wirkt sich auf die Eigenschaften des Systems aus, z.B. auf seine Laufzeitleistung.

– Bass et al., Software Architecture in Practice, 4. Auflage

Eines der am meisten unterschätzten Qualitätsmerkmale von Software ist ihre Vorhersehbarkeit. In der Literatur wird oft betont, dass Software schnell, zuverlässig, robust, beobachtbar, sicher sein sollte. Vorhersehbarkeit zählt selten zu den fünf Top-Prioritäten bei der Entwicklung. Stellen Sie sich als Gedankenexperiment vor, Sie würden objektorientierte Software nach einem einzigen Prinzip entwerfen (wie in Abbildung 2-1 dargestellt): »Jedes Domänenobjekt muss durch ein einzelnes Objekt im berechenbaren Modell dargestellt werden und umgekehrt.« Versuchen Sie dann, alle Entwurfsregeln, Heuristiken und Rezepte aus dieser Prämisse abzuleiten, um Ihre Software gemäß den Rezepten dieses Buchs vorhersehbar zu machen.

Abbildung 2-1: Es besteht eine 1:1-Beziehung zwischen den Objekten des Modells und den Entitäten der realen Welt

Das Problem

Beim Lesen der Rezepte für Clean Code werden Sie feststellen, dass die meisten in der Industrie verwendeten Sprachimplementierungen darauf verzichten, das gesamte Softwaredesign auf einem einzigen Axiom aufzubauen. Das führt zu erheblichen Problemen. Die meisten modernen Sprachen entstanden, um Implementierungsprobleme zu lösen, die vor drei oder vier Jahrzehnten auftraten, als Ressourcen knapp und Berechnungen vom Programmierer zu optimieren waren. Heute treten solche Probleme nur noch in wenigen Bereichen auf. Die Rezepte in diesem Buch werden Ihnen helfen, diese Probleme zu erkennen, zu verstehen und anzugehen.

Modelle als Retter in der Not

Bei der Entwicklung von Modellen jeder Art ist es entscheidend, die Bedingungen der realen Welt zu simulieren. Sie können jedes Element, für das Sie sich interessieren, in der Simulation verfolgen und auf unterschiedlicher Weise stimulieren, um zu beobachten, ob es sich in gleicher Weise verändert wie in der realen Welt. Meteorologen nutzen mathematische Modelle, um das Wetter vorherzusagen, und viele andere wissenschaftliche Disziplinen stützen sich ebenfalls auf Simulationen. Die Physik sucht nach vereinheitlichenden Modellen, die die Regeln der realen Welt erklären und deren Verhalten vorhersagen. Mit dem Aufkommen des maschinellen Lernens werden auch opake Modelle erstellt, um Verhalten in der realen Welt zu visualisieren.

Die Bedeutung der Bijektion

In der Mathematik ist eine Bijektion eine eineindeutige Funktion, die jedes Element der Definitionsmenge auf genau ein Element der Zielmenge abbildet. Umgekehrt ordnet sie jedes Element der Zielmenge genau einem Element der Definitionsmenge zu. Mit anderen Worten: Eine Bijektion ist eine Funktion, die eine 1:1-Beziehung zwischen den Elementen zweier Mengen herstellt.

Ein Isomorphismus ist hingegen eine stärkere Form der Korrespondenz zwischen zwei mathematischen Strukturen, die die Struktur der zugehörigen Objekte bewahrt. Insbesondere ist ein Isomorphismus eine bijektive Funktion, die zusätzlich die Operationen der Strukturen bewahrt.

Eine Bijektion ist also eine eineindeutige Zuordnung zwischen zwei Mengen. Übertragen auf den Bereich der Software bedeutet dies, dass es immer nur ein einziges Objekt geben sollte, das eine reale Entität repräsentiert. Schauen wir uns an, was passiert, wenn man sich nicht an die Grundsätze der Bijektion hält.

Häufige Fälle, bei denen gegen das Bijektionsprinzip verstoßen wird

Es gibt vier häufige Fälle, bei denen das Bijektionsprinzip verletzt wird.

Fall 1

In Ihrem Modell gibt es ein Objekt, das mehr als eine reale Entität repräsentiert. Viele Programmiersprachen modellieren beispielsweise algebraische Maße unter Verwendung nur des skalaren Betrags. In einem solchen Szenario passiert Folgendes, wie in Abbildung 2-2 dargestellt.

Man kann

10 Meter

und

10 Zoll

(zwei völlig unterschiedliche Entitäten in der realen Welt) durch ein einziges Objekt (

die Zahl 10

) darstellen.

Man könnte sie im Modell einfach addieren, wobei dann die

Zahl 10

(für

10 Meter

) und die

Zahl 10

(für

10 Zoll

) zusammen die

Zahl 20

ergeben, bei der vollkommen unklar ist, was sie repräsentiert.

Abbildung 2-2: Die Zahl 10 repräsentiert unterschiedliche Entitäten der realen Welt.

Die Bijektion ist gebrochen: Das führt zu Problemen, die nicht immer rechtzeitig erkannt werden. Da es sich um ein semantisches Problem handelt, wirkt sich der Fehler oft erst nach längerer Zeit aus, wie im berühmten Fall des Mars Climate Orbiters.

Mars Climate Orbiter

Der Mars Climate Orbiter war eine robotische Raumsonde, die 1998 von der NASA mit dem Ziel gestartet wurde, das Klima und die Atmosphäre des Mars zu untersuchen. Die Mission scheiterte schließlich wegen eines Problems mit dem Leit- und Navigationssystem. Die Triebwerkssteuerung des Raumschiffs war so programmiert, dass sie metrische Krafteinheiten verwendete, während das Bodenkontrollteam mit angloamerikanischen Krafteinheiten operierte. Dieser Fehler führte dazu, dass sich das Raumschiff der Oberfläche des Planeten zu weit näherte und beim Eindringen in die Marsatmosphäre zerstört wurde. Das Problem bestand also darin, dass die Maßeinheiten nicht aufeinander abgestimmt bzw. korrekt umgerechnet wurden, was zu einem katastrophalen Fehler bei der Berechnung der Flugbahn des Raumschiffs führte. Das Scheitern der Mission war ein großer Rückschlag für die NASA, kostete die Raumfahrtagentur 125 Millionen Dollar und hatte einer Reihe von Änderungen bei der NASA zur Folge, einschließlich der Gründung einer neuen Abteilung für Missionssicherheit (siehe »Versteckte Annahmen explizit machen« auf Seite 263).

Fall 2

Das Modell stellt dieselbe reale Einheit durch zwei Objekte dar. Angenommen, in der realen Welt existiert eine Athletin namens Erika Mustermann, die in einer bestimmten Disziplin antritt, aber zugleich in einer anderen Disziplin als Kampfrichterin fungiert. Eine einzelne Person in der realen Welt sollte im berechenbaren Modell durch ein einzelnes Objekt abgebildet werden. Sie müssen nur das minimal notwendige Verhalten modellieren, um eine partielle Simulation zu erstellen.

Wenn es zwei verschiedene Objekte gibt (eine Teilnehmerin und eine Kampfrichterin), die die reale Erika Mustermann repräsentieren, wird es früher oder später zu Inkonsistenzen kommen, wenn Sie einem der beiden Objekte eine Verantwortlichkeit zuweisen, die sich im anderen nicht widerspiegelt (wie in Abbildung 2-3 dargestellt).

Abbildung 2-3: Erika Mustermann wird im Modell durch zwei verschiedene Entitäten dargestellt.

Fall 3

Ein Bitcoin-Wallet könnte als anämisches Objekt mit einigen wenigen Eigenschaften in Bezug auf Adresse, Kontostand usw. dargestellt werden (siehe »Anämische Objekte in Rich Objects konvertieren« auf Seite 36), aber auch als Rich Object, das für den Empfang von Transaktionen, das Schreiben in eine Blockchain, die Ausgabe und Fortschreibung des Kontostands usw. zuständig ist. Egal wie das Objekt letztlich ausgestaltet wird, es wäre in beiden Fällen eine Repräsentation desselben realen Konzepts.

Sie sollten Entitäten nicht als Datenstrukturen mit Attributen betrachten, sondern als Objekte, und erkennen, dass es sich um ein und dasselbe Objekt handelt, das je nach Kontext, in dem es interagiert, unterschiedliche Rollen erfüllt. Kapitel 3 enthält mehrere Rezepte, um Objekte besser zu reifizieren und in Verhaltenseinheiten (Behavorial Entities) umwandeln zu können.

Objektreifikation

Die Objektreifikation oder Objektverdinglichung ist ein Prozess, bei dem abstrakten Konzepten oder Ideen eine konkrete Form gegeben wird, um spezifische Konzepte oder Ideen darzustellen und anämischen und datenorientierten Objekten Verhalten zu verleihen. Dadurch kann man mit diesen Konzepten systematisch und strukturiert arbeiten und sie manipulieren.

Fall 4

In den meisten modernen objektorientierten Programmiersprachen kann aus den Angaben Tag, Monat und Jahr ein Datum konstruiert werden. Wenn Sie das mit Angaben für einen »31. November 2023« versuchen, werden viele gängige Programmiersprachen in milder Nachsicht ein gültiges Objekt zurückgeben (wahrscheinlich den »1. Dezember 2023«).

Dieses Verhalten wird als Vorteil dargestellt, verdeckt aber bestimmte Fehler, die beim Einlesen von Daten auftreten können. Das kann beispielsweise während eines nächtlichen Batchlaufs passieren, wenn solche ungültigen Datumsangaben – weit »entfernt« von der Ursache – verarbeitet werden, wodurch das Fail-Fast-Prinzip verletzt wird (siehe Kapitel 13).

Fail-Fast-Prinzip

Das Fail-Fast-Prinzip (»Prinzip des frühzeitigen Scheiterns«) besagt, dass Sie die Ausführung so früh wie möglich unterbrechen sollten, wenn ein Fehler auftritt, anstatt ihn zu ignorieren und dadurch einen erst später auftretenden Fehler in Kauf zu nehmen.

Der Einfluss von Sprache auf die Modellierung

In diesem Buch geht es um die Verbesserung von »schmutzigem«, nicht-deklarativem, kryptischem und vorzeitig optimiertem Code. Das Wissen über die Welt wird durch die Sprache geformt, die man spricht, und man braucht gute Metaphern für seine Objekte und Verhaltensweisen, wie es die Sapir-Whorf- Hypothese postuliert.

Sapir-Whorf-Hypothese

Die Sapir-Whorf-Hypothese, auch bekannt als Theorie der sprachlichen Relativität, besagt, dass die Struktur und der Wortschatz der Sprache eines Menschen seine Wahrnehmung der Welt beeinflussen und formen können. Die eigene Sprache reflektiert und repräsentiert nicht nur die Realität, sondern spielt auch eine Rolle bei ihrer Gestaltung und Konstruktion. Das bedeutet, dass die Art und Weise, wie man die Welt »denkt« und erlebt, durch die Sprache, mit der man sie beschreibt, mitbestimmt wird.

Wenn Sie Objekte als reine Datencontainer betrachten, verlieren Ihre Modelle die Bijektivität und verletzen die MAPPER-Prinzipien. Viele Rezepte dazu finden Sie in Kapitel 3. Wenn Sie Objekte als Datencontainer behandeln, wird Ihr Modell (die Software, die Sie erstellen) nicht in der Lage sein, die reale Welt genau vorherzusagen und zu simulieren, und wird zu Softwarefehlern führen (oft und unpassend als Bugs bezeichnet). Und Ihre Kunden werden feststellen, dass die Software sie nicht mehr korrekt bei ihrer Arbeit unterstützt.

Bug

Der Begriff Bug ist ein in der Industrie weit verbreiteter missverständlicher Begriff. In diesem Buch spreche ich stattdessen von Defekten oder einfach von Fehlern. Der Begriff »Bug« (wörtlich »Wanze» oder »Ungeziefer») stammt aus einer Zeit, als bei den ersten Computern Insekten von außen in die Schaltkreise eindrangen und zu Fehlern und falschen Ausgaben führten. Das kommt heutzutage nicht mehr vor. Ich empfehle, stattdessen den Begriff Defekt zu verwenden, da er sich auf etwas bezieht, das wir selbst in unsere Programme eingebracht haben, und nicht auf einen Eindringling von außen zurückgeht.

KAPITEL 3

Anämische Modelle

Korrektheit ist die entscheidende Eigenschaft. Wenn ein System nicht das tut, was es soll, dann wird alles andere nebensächlich.

– Bertrand Meyer, Object-Oriented Software Construction(dt. Objektorientierte Softwareentwicklung)

Einführung

Anämische Domänenmodelle, oder einfach anämische Objekte, sind Objekte, die lediglich eine Ansammlung von Attributen sind, ohne echtes Verhalten zu besitzen. Ein anämisches Objekt wird oft als »Datenobjekt« bezeichnet, da es hauptsächlich dem Speichern von Daten dient, jedoch keine sinnvollen Methoden oder Operationen aufweist, die mit diesen Daten ausgeführt werden können.

Es kann das Prinzip der Kapselung verletzen, wenn man Daten über Getter und Setter öffentlich verfügbar macht, weil auf diese Weise externen Parteien erlaubt wird, auf die Daten eines Objekts zuzugreifen und sie möglicherweise zu verändern, anstatt ihre Verwaltung ausschließlich im Objekt selbst zu belassen. Das macht ein Objekt anfälliger für Beschädigungen oder unbeabsichtigte Änderungen.

Zudem fördert ein anämischer Entwurfsansatz einen eher prozeduralen Programmierstil, bei dem die Manipulation von Daten im Vordergrund steht, anstatt die Daten in Objekten mit bedeutungsvollem Verhalten zu kapseln. Mit diesem Buch möchte ich Sie ermutigen, Rich Objects zu erstellen: Rich Objects verfügen über einen robusteren Satz von Verhaltensweisen und Methoden, um sinnvolle Operationen durchzuführen und einen singulären Zugriffspunkt zu bieten, sodass keine Logik dupliziert wird.

Kapselung

Kapselung zielt darauf ab, die Verantwortlichkeiten eines Objekts zu schützen. Dies lässt sich in der Regel durch Abstraktion der eigentlichen Implementierung erreichen. So lässt sich auch der Zugriff auf die Methoden eines Objekts steuern. In vielen Programmiersprachen kann man die Sichtbarkeit der Eigenschaften und Methoden eines Objekts festlegen und dadurch kontrollieren, wie und ob andere Programmteile darauf zugreifen oder sie ändern können. Das erlaubt Entwicklern, die internen Implementierungsdetails eines Objekts zu verbergen und nur dasjenige Verhalten offenzulegen, das von anderen Teilen des Programms benötigt wird.

Anämische Objekte in Rich Objects konvertieren

Problem

Sie möchten Ihre Objekte vor externen Eingriffen schützen und Verhalten statt Daten und Strukturen offenlegen, um die Ausbreitung von Änderungen einzuschränken.

Lösung

Setzen Sie die Sichtbarkeit aller Attribute auf private.

Diskussion

Stellen Sie sich vor, eine Domäne hätte sich weiterentwickelt und Sie müssten mit den Geschäftsregeln Ihrer Kunden Schritt halten. Ein Beispiel aus der Musikindustrie wäre die zusätzliche Anforderung, alle Titel einem Genre zuzuordnen und wesentliche Eigenschaften zu schützen. Betrachten wir eine Klassendefinition für die Metadaten eines Musiktitels:

public class Song {

String name;

String authorName;

String albumName;

}

In diesem Beispiel wird Song als Tupel dargestellt, mit dem Ziel, seine Attribute an verschiedenen Stellen im Code zu manipulieren, um den Künstler oder das Album bearbeiten zu können.

Ändern Sie die Sichtbarkeit der Attribute von public auf private:

public class Song {

private String name;

private Artist author; // Referenziert Rich Objects

private Album album; // anstelle von primitiven Datentypen.

public String albumName() {

return album.name() ;

}

}

Durch die Änderung der Sichtbarkeit auf private müssen Sie das öffentliche Verhalten von Song nutzen, was die Kapselung verstärkt.

Auf Attribute können Sie jetzt erst zugreifen, wenn Sie dafür neue Methoden hinzufügen. Externe Manipulationen werden typischerweise an unterschiedlichen Stellen in einem System vorgenommen (siehe »Wiederholten Code entfernen« auf Seite 149). Wenn Ihre Objekte öffentlich zugängliche Attribute haben, können sie sich unerwartet verändern.

Gemäß dem in Kapitel 2 definierten MAPPER sollten Sie Objekte basierend auf ihrem Verhalten und nicht als anämische Objekte entwerfen, die ausschließlich Daten modellieren. Beachten Sie, dass in einigen Sprachen wie Java, C++, C# oder Ruby die Sichtbarkeit von Attributen festgelegt werden kann, während in anderen Sprachen wie beispielsweise JavaScript diese Option fehlt. Einige Sprachen, wie Python oder Smalltalk, folgen bestimmten Konventionen, bei denen die Sichtbarkeit dokumentiert, aber nicht erzwungen wird. Das widerspricht der Definition von sicherem Refactoring aus Kapitel 1. Die Änderung der Sichtbarkeit eines Attributs kann bestehende Abhängigkeiten unterbrechen.

Die Anwendung dieses und vieler anderer Rezepte setzt eine umfassende Testsuite voraus, die als Sicherheitsnetz fungiert, um mögliche neu eingeführte Fehler zu identifizieren. Michael Feathers erläutert in seinem Buch Working Effectively with Legacy Code (dt. Effektives Arbeiten mit Legacy Code), wie ein solches Sicherheitsnetz erstellt und genutzt wird.

Verwandte Rezepte

»Automatische Eigenschaften entfernen« auf Seite 42

»Wiederholten Code entfernen« auf Seite 149

Das Wesentliche Ihrer Objekte erkennen

Problem

Sie möchten Invarianten (siehe »Vorbedingungen durchsetzen« auf Seite 191) für Ihre Objekte definieren und sicherstellen, dass diese eingehalten werden.

Lösung

Verhindern Sie Änderungen an den wesentlichen Eigenschaften oder Verhaltensweisen. Legen Sie die wesentlichen Attribute bei der Objekterstellung fest und schützen Sie sie vor späteren Änderungen.

Diskussion

Jede reale Entität besitzt essenzielles Verhalten, das seine Identität ausmacht: Es ist genau dieses Objekt und kein anderes. Dieses Verhalten findet sich in der Bijektion, die Objekt und reale Welt aufeinander abbildet. Die Essenz ist die DNA eines Objekts. Einmal erzeugt, lässt sich die Essenz eines Objekts nicht mehr verändern. Objekte können sich in akzidentellen, nebensächlichen Aspekten ändern, aber nicht in ihrem Wesenskern.

Essenz und Zufall

In seinem Buch The Mythical Man-Month (dt. Vom Mythos des Mann-Monats) verwendet Fred Brooks die Begriffe »accidental« (zufällig, sekundär) und »essential« (essenziell, wesentlich), um zwei Arten von Komplexität in der Softwareentwicklung zu beschreiben, angelehnt an Aristoteles’ Definition.

»Wesentliche« Komplexität ist dem zu lösenden Problem inhärent und unvermeidlich, da sie notwendig ist, damit das System wie beabsichtigt und wie in der realen Welt funktioniert. Beispielsweise ist die Komplexität des Landesystems einer Raumsonde essenziell, da diese Komponente für die sichere Landung eines Rovers unentbehrlich ist.

»Zufällige« oder »nicht wesentliche« Komplexität ergibt sich aus der Art und Weise, wie ein System entworfen und implementiert wird, und nicht aus der Natur des Problems selbst. Zufällige Komplexität lässt sich durch gutes Design reduzieren. Sie stellt eine der größten Herausforderungen in der Softwareentwicklung dar, und in diesem Buch finden Sie viele Ansätze, um sie zu minimieren oder zu vermeiden.

Schauen wir uns an, was passiert, wenn man den Monat eines Datumsobjekts (Date) ändert:

In der realen Welt findet man keine direkte Entsprechung für die isolierte Änderung des Monats eines Datums, da der Monat eine wesentliche Eigenschaft eines jeden Datums ist. Dennoch erlauben viele Programmiersprachen diese Art der Modifikation. Beim Aufruf von setMonth() wird nur der Wert für den Monat aktualisiert, während die Werte für Tag und Jahr gleich bleiben.

Alle anderen Objekte, die sich auf dieses Datum beziehen, ändern sich jetzt kaskadierend, ähnlich einer sich ausbreitenden Welle (Ripple-Effekt). Nehmen wir an, es gäbe eine finanzielle Forderung, die an einem zuvor festgelegten Datum fällig wird. Wird nun dieses Datum geändert, wird davon unweigerlich auch die Zahlung beeinflusst. Eine bessere Lösung wäre es, eine bestehende Referenz auf payment auf ein neues Datum verweisen zu lassen, indem man eine Methode wie defer() aufruft. Diese Änderung hätte dann nur begrenzte Auswirkungen auf das payment-Objekt.

Ripple-Effekt

Ein Ripple-Effekt (oder Welleneffekt) tritt auf, wenn eine Änderung oder Modifikation an einem Teil eines Systems unbeabsichtigte Auswirkungen auf andere Teile des Systems hat. Eine Änderung an einem Objekt kann Auswirkungen auf Systemteile haben, die von diesem Objekt abhängig sind, und dort zu Folgefehlern oder unerwartetem Verhalten führen.

Das wäre eine bessere Lösung:

Sobald das Datum erstellt wurde, ist es unveränderlich. Sie können darauf vertrauen, dass es stets derselben realen Entität zugeordnet bleibt. Es ist wichtig, im Modell festzulegen, welche Attribute und Verhaltensweisen essenziell und welche akzidentell sind: Was in der realen Welt wesentlich ist, muss auch in Ihrem Modell wesentlich sein und umgekehrt.

Viele moderne Programmiersprachen versäumen es, das Wesen von Objekten zu identifizieren und zu schützen. Die Klasse Date ist ein gängiges Beispiel dafür. Für die meisten Sprachen stehen jedoch auch alternative, robuste Pakete zur Manipulation von Kalenderdaten zur Verfügung.

Verwandte Rezepte

»Veränderungen der Objektessenz verbieten« auf Seite 76

»Ripple-Effekt vermeiden« auf Seite 281

Objekte von Settern befreien

Problem

Sie möchten Ihre Objekte vor externen Manipulationen durch Setter – auch als Zugriffsfunktionen oder Akzessoren bekannt – schützen und bevorzugen Unveränderbarkeit.

Lösung

Nachdem Sie die Sichtbarkeit Ihrer Attribute auf private geändert haben (siehe »Anämische Objekte in Rich Objects konvertieren« auf Seite 36), entfernen Sie alle Setter.

Diskussion

Hier ein klassisches Beispiel für eine Point-Klasse mit Settern:

Hier ist eine kürzere Version, nach Anwendung des Rezepts und Entfernung der Setter:

Setter begünstigen Mutabilität (siehe Kapitel 5) und anämische Modelle. Sie sollten Änderungen an Ihren Objekten ausschließlich durch den Aufruf einer Methode vornehmen, deren Sekundäreffekt die gewünschte Änderung ist. Dies folgt dem Prinzip »Tell, don’t ask«.

»Tell, don’t ask«-Prinzip

Das »Tell, don’t ask«-Prinzip definiert eine Interaktionsweise mit Objekten, bei der Methoden aufgerufen werden, anstatt nach Daten zu fragen.

Das Hinzufügen von Settern zu einem Objekt ermöglicht unerwartete Veränderungen, und Sie sind gezwungen, an verschiedenen Stellen des Systems invariante Integritätskontrollen hinzuzufügen. Das wiederum führt zu Duplizierung (siehe »Wiederholten Code entfernen« auf Seite 149). Methoden mit dem Namen setXXX() verletzen das MAPPER-Prinzip, da es dazu selten eine Entsprechung in der realen Welt gibt. Die Essenz von Objekten zu verändern, sollte vermieden werden (vgl. das Kapitel 4). Viele Sprachen ermöglichen die Manipulation von Datumswerten (z.B. mit date.setMonth(5)), aber solche Operationen beeinflussen alle darauf basierenden Objekte.

Verwandte Rezepte

»Automatische Eigenschaften entfernen« auf Seite 42

»Leere Konstruktoren vervollständigen« auf Seite 46

»Getter entfernen« auf Seite 48

Auf Generatoren verzichten, die anämischen Code produzieren

Problem

Sie nutzen Codegeneratoren, möchten jedoch mehr Kontrolle über Ihre Attribute haben, Rich Objects verwenden, Duplizierung vermeiden und den Fokus auf Verhalten statt auf Daten legen.

Lösung

Verzichten Sie auf Codeassistenten und -generatoren. Wenn Sie sich wiederholende Arbeiten vermeiden möchten, wenden Sie das Rezept »Wiederholten Code entfernen« auf Seite 149 an und legen ein Zwischenobjekt an, das das wiederkehrende Verhalten enthält.

Diskussion

Codeassistenten waren in den 90er-Jahren sehr beliebt. Projekte wurden oft nach der Anzahl ihrer Codezeilen bewertet, und eine große Codebasis galt als erstrebenswert. Man verwendete automatische Codegeneratoren, die nur eine Klassenvorlage mit Attributen benötigten. Das führte zu Codeduplizierung und schwer wartbaren Systemen.

Heute erstellen Codeassistenten wie Codex, Code Whisperer, ChatGPT oder GitHub Copilot auf Anfrage Code auf ähnliche Weise. Wie bereits in früheren Rezepten besprochen, sollten Sie anämischen Code vermeiden – genau das aber produzieren KI-Codeassistenten auf dem aktuellen Stand der Technik.

Hier ein Beispiel für eine anämische Klasse, die durch Metaprogrammierung erstellt wurde (siehe Kapitel 23):

AnemicClassCreator::create(

'Employee',

[

new AutoGeneratedField(

'id', '$validators->getIntegerValidator()'),

new AutoGeneratedField(

'name', '$validators->getStringValidator()'),

new AutoGeneratedField(

'currentlyWorking', '$validators->getBooleanValidator()')

]);

Metaprogrammierung erzeugt automatisch magische Setter und Getter:

getId(), setId(), getName(), ….

// Validierung ist nicht explizit.

Die Klasse wird per Autoloader geladen:

Um dieses Rezept anzuwenden, sollten Sie den Code explizit, lesbar und debugfähig gestalten:

Es mag mühsam erscheinen, aber es ist viel besser, fehlende Abstraktionen zu identifizieren und expliziten Code zu verwenden, als anämischen Code zu erzeugen.

Verwandte Rezepte

»Wiederholten Code entfernen« auf Seite 149

»Metaprogrammierung entfernen« auf Seite 365

Automatische Eigenschaften entfernen

Problem