Objektorientierter Entwurf
Überblick
Das Paket enthält Entwurfsregeln für Codebasen, die in einem objektorientierten Stil geschrieben sind, kein universeller Rat. Die eigenen schriftlichen Konventionen eines Projekts haben Vorrang vor dem Paket. Folgen Sie dem Idiom des Moduls, das geändert wird. Diese Fähigkeit dimensioniert die Aufgabe und benennt die Fähigkeit, die passt.
Laden Sie die Fähigkeit, die dieser Index nennt, bevor Sie antworten: Ihre Regeln und Beispiele leben dort, nicht hier.
Wie viel Entwurf eine Aufgabe rechtfertigt
| Aufgabe | Verwenden | Überspringen |
|---|---|---|
| Eine Antwort, die einmal verwendet und dann weggeworfen wird | Nichts | Alles |
| Eine Methode in einer Klasse, die existiert | Ihr Name und ihr Vertrag | Umgestaltung |
| Eine Funktion innerhalb eines bestehenden Ablaufs | Das Idiom, das dieser Ablauf verwendet | Neue Grenzen |
| Eine neue Klasse oder Modul | Die Entwurfsschleife | Nichts |
| Ein neuer Kontext | Die Schleife, dann ihre Grenzen | Nichts |
| Ein neues System aus dem Nichts | Die Schleife, Kontexte, dann Struktur | Nichts |
Vor dem Entwurf
Vor dem Entwurf geben Sie das Tor an: eine Regel, die die Software befolgen muss, mit einem Beispiel, das sie akzeptiert, und einem, das sie ablehnt. Zum Beispiel: „Ein Slot kann höchstens einen Termin enthalten.“ Es akzeptiert, einen zweiten Patienten in einen anderen Slot zu buchen, und lehnt es ab, einen zweiten Patienten in denselben Slot zu buchen. Wenn Sie die Regel aufschreiben können, beginnen Sie. Wenn nicht, fehlt etwas, das nicht der Entwurf ist, und dieses Paket kann es nicht liefern.
Die Entwurfsschleife
- Sagen Sie, was die Software in den Worten der Domäne tun muss (die Domänensprache sprechen), und finden Sie das Objekt, das weiß, was jedes Verhalten braucht (Verantwortlichkeiten zuweisen).
- Finden Sie die Konsistenzgrenze: was in einer Transaktion zusammen wahr sein muss (Aggregate entwerfen).
- Entscheiden Sie, wo jede Regel lebt: eine Entität, ein Wertobjekt oder ein Domänendienst (Domänenlogik platzieren).
- Entwerfen Sie die Nachrichten, die jedes Objekt beantwortet (Zustand und Verhalten kapseln), und machen Sie illegale Zustände unmöglich zu konstruieren (illegale Zustände unmöglich machen).
- Kehren Sie Abhängigkeiten um, sodass die Domäne nichts von der Datenbank oder dem Framework weiß (Abhängigkeiten umkehren, das System schichten).
- Benennen Sie Dinge in den Worten der Domäne und kommentieren Sie nur das, was der Code nicht sagen kann (Klassen und Methoden benennen, kommentieren, was der Code nicht sagen kann).
- Überprüfen Sie das Ergebnis anhand der roten Flaggen unten (Objektentwurf überprüfen).
Wie streng eine Regel gemeint ist
Jede Kernregel in diesem Paket trägt eines von drei Etiketten, und das Etikett sagt, wie streng sie gemeint ist.
- Regel. Ein Verstoß ist ein Fehler. Kein Framework entschuldigt ihn.
- Standard. Tun Sie dies, es sei denn, eine angegebene Bedingung gilt. Die Bedingung ist ihr eigener Satz, niemals eine nachgestellte Klausel.
- Beurteilung. Zwei Antworten sind vertretbar. Die Fähigkeit sagt, was die Waage kippt.
Symptome zu Fähigkeiten
Symptome verweisen auf die Fähigkeit, die sie behandelt, und diese Tabelle wächst, wenn Fähigkeiten hinzukommen.
| Symptom | Fähigkeit |
|---|---|
| Welche Klasse sollte das tun? | Verantwortlichkeiten zuweisen |
| Diese Klasse tut zu viel | Verantwortlichkeiten zuweisen |
| Eine Änderung wirkt sich auf viele Klassen aus | Kopplung diagnostizieren |
| Wie eng sind diese beiden Teile verbunden? | Kopplung diagnostizieren |
| Ist das ein Objekt oder nur Daten? | Objekt oder Daten entscheiden |
| Unser Domänenmodell ist anämisch | Objekt oder Daten entscheiden |
| Anderer Code entscheidet für dieses Objekt | Zustand und Verhalten kapseln |
| Setter lassen dieses Objekt seine Regeln brechen | Zustand und Verhalten kapseln |
| Dieses Objekt kann halb aufgebaut existieren | Gültige Objekte konstruieren |
| Dieser Konstruktor nimmt zu viele Argumente | Gültige Objekte konstruieren |
| Jeder neue Fall bedeutet, funktionierenden Code zu bearbeiten | Was variiert, einkapseln |
| Wie viel Flexibilität sollte dies haben? | Was variiert, einkapseln |
| Ist das ein Wert oder eine Entität? | Werte und Entitäten modellieren |
| Rohe Zeichenketten und Zahlen tragen Domänenregeln | Werte und Entitäten modellieren |
| Sollte dies jene Klasse erweitern oder sie halten? | Komposition über Vererbung wählen |
| Das Ändern einer Basisklasse bricht ihre Unterklassen | Komposition über Vererbung wählen |
| Eine Unterklasse bricht, was ihr Elternteil verspricht | Substituierbarkeit einhalten |
| Code überprüft, welchen Subtyp er hält | Substituierbarkeit einhalten |
| Die gleiche Switch-Anweisung über einen Typ kommt immer wieder | Bedingungen durch Polymorphie ersetzen |
| Unterklassen oder Pattern Matching? | Bedingungen durch Polymorphie ersetzen |
| Code reicht durch eine Kette von Objekten | Begrenzen, was ein Objekt weiß |
| Diese Klasse leitet nur Aufrufe weiter | Begrenzen, was ein Objekt weiß |
| Wie sollte diese Klasse oder Methode heißen? | Klassen und Methoden benennen |
| Namen wie Manager, Helper oder Util | Klassen und Methoden benennen |
| Aufrufer hängen von Schnittstellenmethoden ab, die sie nie verwenden | Schnittstellen entwerfen |
| Schnittstelle oder abstrakte Basisklasse? | Schnittstellen entwerfen |
| Domänencode hängt von einer Datenbank oder einem Framework ab | Abhängigkeiten umkehren |
| Wo werden Objekte erstellt und verdrahtet? | Abhängigkeiten umkehren |
| Aufrufer wissen, wie eine andere Klasse Daten speichert oder formatiert | Implementierung verbergen |
| Eine Klasse gibt ihre internen Fehler oder Schritte preis | Implementierung verbergen |
| Flags oder optionale Felder erlauben unmögliche Kombinationen | Illegale Zustände unmöglich machen |
| Ein Typ lässt Werte zu, die die Domäne nie hat | Illegale Zustände unmöglich machen |
| Die Worte des Codes unterscheiden sich von denen des Personals | Die Domänensprache sprechen |
| Ein Wort bedeutet für zwei Teams unterschiedliche Dinge | Die Domänensprache sprechen |
| Welche Objekte müssen sich zusammen ändern? | Aggregate entwerfen |
| Speichervorgänge kollidieren oder ein Laden zieht zu viel | Aggregate entwerfen |
| Welche Statusänderungen sind erlaubt? | Lebenszyklen modellieren |
| Ein abgebrochenes oder geschlossenes Objekt ändert sich erneut | Lebenszyklen modellieren |
| Wie sollten Domänenobjekte gespeichert und gefunden werden? | Repositories entwerfen |
| Das Repository hat eine Find-Methode pro Bildschirm | Repositories entwerfen |
| Anwendungsfälle wiederholen die gleiche Geschäftsprüfung | Domänenlogik platzieren |
| Domänendienst, Entitätsmethode oder Anwendungsdienst? | Domänenlogik platzieren |
| Andere Teile müssen reagieren, nachdem eine Änderung gespeichert wurde | Domänenereignisse veröffentlichen |
| Eine Nachricht wurde für eine Änderung gesendet, die zurückgesetzt wurde | Domänenereignisse veröffentlichen |
| Wo endet das Modell eines Teams und das eines anderen beginnt? | Kontexte abgrenzen |
| Welche Teile sind Kern und welche können wir kaufen? | Kontexte abgrenzen |
| In welche Richtung sollten die Schichten abhängen? | Das System schichten |
| Ein Bildschirm oder Job kann nicht ohne sein Framework getestet werden | Das System schichten |
| Module hängen zyklisch voneinander ab | Module organisieren |
| Modularer Monolith oder separate Dienste? | Module organisieren |
| Eine Änderung an unserer API bricht die Apps, die sie aufrufen | Systemgrenzen überschreiten |
| Wie sollten wir diese API oder Nachricht versionieren? | Systemgrenzen überschreiten |
| Das Mapping-Werkzeug formt unsere Domänenklassen | Persistenz bändigen |
| Eine Liste führt eine Abfrage pro Zeile durch | Persistenz bändigen |
| Bildschirme benötigen Daten, die anders geformt sind als das Modell | Lesevorgänge von Schreibvorgängen trennen |
| Read Model, Projection oder Event Sourcing? | Lesevorgänge von Schreibvorgängen trennen |
| MVC, MVP oder MVVM für diesen Bildschirm? | Darstellung von Domäne trennen |
| Ein Bildschirm bearbeitet Domänenobjekte direkt | Darstellung von Domäne trennen |
| Welches Entwurfsmuster passt zu diesem Problem? | Ein Muster wählen |
| Ein Muster wurde vor seinem Problem hinzugefügt | Ein Muster wählen |
| Diese Musterklassen könnten eine Funktion sein | Muster, die die Sprache bereitstellt |
| Visitor oder eine geschlossene Menge mit Matching? | Muster, die die Sprache bereitstellt |
| Eine Methode gibt null zurück, wenn sie nichts findet | Fehlerverhalten entwerfen |
| Ablehnungen kommen als Codes zurück, die Aufrufer ignorieren | Fehlerverhalten entwerfen |
| Threads teilen ein Objekt und verlieren oder vermischen seine Änderungen | Gemeinsamen Zustand verwalten |
| Ein Lock oder ein einzelner Besitzer für diesen Zustand? | Gemeinsamen Zustand verwalten |
| Ein Aufruf an ein anderes System hängt oder schlägt fehl | Arbeit zuverlässig machen |
| Ein Wiederholungsversuch hat die Arbeit zweimal erledigt | Arbeit zuverlässig machen |
| Wir können einen Vorfall nicht aus unseren Aufzeichnungen erklären | Entwerfen, was aufgezeichnet wird |
| Was sollten wir protokollieren, zählen oder prüfen? | Entwerfen, was aufgezeichnet wird |
| Eine Überprüfung fand eine Gottklasse oder einen Lavafluss | Antipatterns erkennen |
| Welches Antipattern sollten wir zuerst beheben? | Antipatterns erkennen |
| Tests brechen, obwohl sich das Verhalten nicht geändert hat | Objektorientierten Code testen |
| Mock, Stub oder Fake für diesen Kollaborateur? | Objektorientierten Code testen |
| Tests hängen von der Zeit oder zufälligen Werten ab | Für Testbarkeit entwerfen |
| Code wird nur öffentlich gemacht, damit Tests ihn erreichen können | Für Testbarkeit entwerfen |
| Wie strukturieren wir diesen Code sicher um? | Hin zum Objektentwurf umgestalten |
| Eine Umgestaltung wurde zu groß, um sie abzuschließen | Hin zum Objektentwurf umgestalten |
| Wir müssen Code ändern, der keine Tests hat | Mit Legacy-Code arbeiten |
| Ersetzen Sie ein altes System Stück für Stück | Mit Legacy-Code arbeiten |
| Was sollte diese Überprüfung prüfen? | Objektentwurf überprüfen |
| Welche Überprüfungsbefunde blockieren einen Merge? | Objektentwurf überprüfen |
| Niemand erinnert sich, warum der Code so geformt ist | Entwurfsentscheidungen dokumentieren |
| Was sollten wir zeichnen und auf welcher Ebene? | Entwurfsentscheidungen dokumentieren |
| Kommentare wiederholen den Code oder widersprechen ihm | Kommentieren, was der Code nicht sagen kann |
| Wo erklären wir, warum dieser Code seltsam aussieht? | Kommentieren, was der Code nicht sagen kann |
| Ein Framework-Hook oder -Marker enthält eine Geschäftsregel | Innerhalb eines Frameworks arbeiten |
| Wie weit sollten wir mit dem Framework gehen? | Innerhalb eines Frameworks arbeiten |
| Eine Berechnung speichert auch, sendet oder ändert ihre Eingaben | Funktionale Techniken anwenden |
| Welche funktionalen Techniken passen zu Objektcode? | Funktionale Techniken anwenden |
Sprachfähigkeiten
Eine Sprachfähigkeit hält die Kernregeln in einer Sprache und ersetzt sie nie. Eine Basis-Sprachfähigkeit ist für Code ohne Framework.
oo-java, ausobject-oriented-design-java: Records, versiegelte Typen,equalsundhashCode, geprüfte Ausnahmen,Optional, und Klassen, die für Erweiterungen geschlossen sind.oo-kotlin, ausobject-oriented-design-kotlin: Datenklassen, versiegelte Typen, nullable Typen, Delegation mitby, und Erweiterungsfunktionen.oo-typescript, ausobject-oriented-design-typescript: Markierte Bezeichner,#private-Zustand, abstrakte Klassen, Dekorateure, diskriminierte Vereinigungen und eine Kompositionswurzel.oo-javascript, ausobject-oriented-design-javascript: Klassenmitglieder mit#, Fabrikfunktionen, optionale Verkettung, Nullish Coalescing, und Module.oo-python, ausobject-oriented-design-python:@dataclass,@property,typing.final, NamedTuple, Abstract Base Classes und @staticmethod.