Prinzipien für gutes Design
SOLID-Prinzipien
Die SOLID-Prinzipien helfen ein robustes und erweiterbares Software-Design zu entwickeln
- Single Responsibility-Prinzip
- Open/Close-Prinzip
- Liskov Substitution-Prinzip
- Interface Segregation-Prinzip
- Dependency Inversion-Prinzip
Single Responsibility-Prinzip: Eine Klasse sollte genau eine Aufgabe haben und alle ihre Dienste sollten genau zu dieser Aufgabe passen
Open/Closed-Prinzip: Eine Klasse sollte offen für Erweiterungen aber geschlossen für Modifikationen sein
An dieser Stelle muss man verstehen, was genau mit Erweiterung und Modifikation gemeint ist.
- Bei einer Erweiterung leitet man von vorhandenen Klassen neue Klassen ab, die durch geschicktes Überschreiben von Methoden die Funktionalität erweitern. Hierzu gibt es passende Entwurfsmuster, von denen wir bereits einige kennengelernt haben (z. B. Template Pattern).
- Bei einer Modifikation wird eine vorhandene Klasse modifiziert, d. h. ihr Quellcode wird verändert.
Einen Verstoß gegen das Open/Closed-Prinzip erkennt man immer daran, wenn man nach dem Anlegen einer neuen Klasse, eine bereits vorher vorhandene Klasse verändert muss.
Liskov Subsititution-Prinzip: Subklassen sollten echte Subklassen im Sinne des liskovschen Substitutionsprinzips sein
Das liskovsche Substitutionsprinzip wurde bereits weiter oben erläutert.
Interface Segregation-Prinzip: Verwender einer Schnittstelle sollten nicht gezwungen werden Interfaces zu implementieren, die sie gar nicht nutzen wollen
Dieses Prinzip besagt, dass Benutzer:innen eines Interfaces nicht mit Methoden konfrontiert werden sollten, die sie nicht benötigen. Jedes Interface sollte sich auf eine einzige Verantwortung beschränken und spezialisiertere Interfaces von einer größeren, allgemeineren Oberklasse abgeleitet werden.
Es ist besser, mehrere kleine, spezialisierte Interfaces zu haben, als ein großes, allgemeines Interface, das viele Methoden enthält, die für bestimmte Klassen nicht relevant sind.
Dependency Inversion-Prinzip: Abstraktionen sollten nicht von Details abhängen, sondern Details von Abstraktionen
Das Dependency Inversion-Prinzip besagt, dass höhere Ebene nicht von darunter liegenden Ebene abhängig sein sollten.
Dies bedeutet, dass man nicht auf konkrete Klassen direkt verweisen sollte, sondern stattdessen auf Abstraktionen, die von diesen Klassen implementiert werden. So kann man eine bessere Trennung zwischen verschiedenen Schichten in einem System erreichen und es wird einfacher, Änderungen in den Implementierungen von Klassen ohne Auswirkungen auf andere Teile des Systems durchzuführen.
Einen Verstoß gegen dieses Prinzip erkennt man daran, dass eine Subtyp in der Schnittstelle eines Supertypen auftaucht. Es wäre also nach diesem Prinzip falsch, wenn in dem Interface Flieger
eine Klasse auftauchen würde, die das Interface implementiert, z. B. Superman
.
STUPID
STUPID-Prinzip
Wenn es Prinzipien für gutes Design gibt, so ist es naheliegend, dass es auch einen Gegenmodell gibt, das beschreibt, woran man ein schlechtes Design erkennen kann.
- Singleton: Verwendung von Singletons mit globalem Zustand
- Tight Coupling: Enge Verzahnung der Komponenten
- Untestability: Testen nicht berücksichtigt
- Premature Optimization: Unnötige Optimierungen
- Indescriptive Naming: Unverständlichen Namen
- Duplication: Kopierter Code
Das STUPID-Prinzip ist ein Akronym für „Singleton, Tight Coupling, Untestability, Premature Optimization, Indescriptive Naming“ und Duplication. Es ist ein Begriff aus der Softwareentwicklung, der häufig im Zusammenhang mit der objektorientierten Programmierung verwendet wird, um auf schlechte Programmierpraktiken hinzuweisen.
- Singleton: Die Anwendung verwendet mehrere Singeltons, die globalen Zustand halten. Durch wird sie schwer testbar und fehleranfällig.
- Tight Coupling: Die Komponenten der Software sind eng miteinander verzahnt und es gibt sehr viele Abhängigkeiten.
- Untestability: Bei der Entwicklung wurde nicht daran gedacht, die Software auch testbar zu gestalten. Es ist schwierig, Unit-Tests zu schreiben und auszuführen.
- Premature Optimization: Die Entwickler:innen versuchen, Code zu optimieren, obwohl noch gar nicht feststeht, dass die Optimierung auch notwendig ist. Dies kann zu unnötigem Code und einer unnötigen Komplexität führen.
- Indescriptive Naming: Die Naben von Klassen und Methoden ist uneinheitlich und beschreibt die Funktion der jeweiligen Komponenten nicht, oder nur unvollständig. Beispiele sind
var1
,var2
,_button1
etc. - Duplication: Der Code enthält viele kopierte und damit duplizierte Code-Fragmente. Bei der Fehlerbehebung werden dann regelmäßig Kopien übersehen, sodass die Fehler dort nicht korrigiert werden.