Einleitung
Die SOLID Prinzipien repräsentieren fünf grundlegende Design Prinzipien die die Softwareentwicklung besser verstehbar, flexibler und Wartburg machen.
Robert C. Martin führte die Theorie der SOLID Prinzipien das erste Mal in seinem Buch Agile Software Development and Principles ein. Später wurde das Buch dann unter dem Titel Agile Principles, Patterns and Practices in C# veröffentlicht. In seinem Buch ging es um Software Design Probleme die Robert C. Martin in seinem Alltag als Softwareentwickler feststellen musste.
Gründe für schlechten Code:
- Zu viel Funktionalität in einer Klasse
- Eng gekoppelte Klasse (Wenn Klassen voneinander abhängen, betreffen Änderungen meiste beide/mehrere Klassen)
Gründe für guten Code:
- Verwendung von Design Pattern wie MVC (Model Vie Controller), MVVM (Model View View Model), Layered, 3-tier,…. for ein entsprechendes Problem.
- Einsatz von Design Prinzipien und Pattern wenn es das Projekt erfordert.
S -> Single Responsibility Principle
O -> Open Closed Principle
L -> Liskov Substitution Principle
I -> Interface Segregation Principle
D -> Dependency Inversion Principle
Was sind die Vorteile beim Verwendung der SOLID Prinzipien?
- Reduzierung der Komplexität von Code
- Lesbarkeit sowie Erweiterbarkeit und Wartbarkeit werden erhöht.
- Reduzierung von Fehlern und Erhöhungen Wiederverwendbarkeit
- Bessere Testbarkeit
- Reduzierung von Abhängigkeiten
S -> Single Responsibility Principle
In SOLID steht das S für Single Responsibility Principle (SRP). SRP bedeutet das jedes Modul oder jede Klasse nur einen Grund bieten sollte sie zu ändern. Das bedeutet im Umkehrschluss das jedes Modul oder jede Klasse nur eine Abhängigkeit haben sollte. Das bedeutet nicht das eine Klasse nur eine Methode oder eine Eigenschaft (Property) haben darf. Es sagt aus, dass mehrere member/Methoden/Funktionen vorhanden sein können solange sie nur eine Verantwortung (responsability) tragen oder eine Funktion haben. Das führt automatisch dazu das Klassen kleiner, sauberer und leichter wartbarer werden.
O -> Open Closed Principle
Software entities wie Module, Klassen oder Funktionen sollten offen für Erweiterungen und geschlossen gegenüber Modifikationen sein. Wenn sich die Anforderungen an ein entity ändern kann dies geändert werden um den neuen Anforderungen genüge zu tun.Diese Formulierung hört sich im ersten Moment etwas wiedersprüchlich an und es gibt auch diverse Interpretationen dieses Prinzips. Es ursprünglich auch von Bertrand Meyer eingeführt und von Robert C. Martin dann übernommen. Wenn man sich die Umsetzung er beiden anschaut sieht man das auch sie sich nicht einig sind. Meiner Meinung nach handelt es sich um ein vorrausschauendes programmieren. Es sollte vermieden werden beispielsweise in einer switch case Abfrage eine bestimmte Anzahl an Vergleichen anzustellen. Stattdessen kann man ein enum befallen und diese abfragen. Dort ist die Erweiterung möglich ohne die Funktionalität der Klasse bedeutend zu verändern.
L -> Liskov Substitution Principle
Das Liskov-Substitutionsprinzip besagt, dass eine Unterklasse immer in der Lage sein muss, ihre Oberklasse zu ersetzen, ohne Fehler oder unerwünschte Verhaltensweisen hervorzurufen.
In diesem Sinne müssen Klassen und Methoden so entwickelt werden, dass alle Objekte des Untertyps perfekt zu den Eigenschaften des Obertyps passen und ohne Probleme gegen andere Objekte des Untertyps ausgetauscht werden können.
Wenn dein Code auf den Typ eines Objekts prüft, das er behandelt, verstößt er gegen dieses Prinzip. Wenn zum Beispiel eine deiner Methoden mit einem Parameter der Klasse Tier aufgerufen werden soll, kann die Methode nicht prüfen, ob das Tier von der Unterklasse Hund oder von der Unterklasse Vogel ist, um zu entscheiden, ob das Tier laufen oder fliegen muss. In diesem Fall sollte der Code so umstrukturiert werden, dass die Methode nichts über die Unterklassen von Tier weiß und jeden möglichen Untertyp von Tier behandeln kann.
I -> Interface Segregation Principle
Das Interface Segregation Principle besagt, dass Clients nicht von Schnittstellen abhängen sollten, die sie nicht verwenden. Mit anderen Worten, besagt es, dass Ihre Klasse, wenn sie eine Methode einer Schnittstelle nicht benötigt, diese nicht implementieren sollte.
Es ist offensichtlich, dass Schnittstellen dem Code viele Vorteile bringen können, wie z. B. Mehrfachvererbung oder die Möglichkeit der einfachen Injektion bei der Programmierung von Unit-Tests. Das Problem, wenn ein Client dazu gezwungen wird, eine Methode zu implementieren, die für die Klasse nutzlos ist, ist, dass es den Code unflexibler und schlechter wartbar macht.
Wie der Name des Prinzips schon sagt, nimmt sich ISP diesem Problem an, indem es die Schnittstellen trennt. Wenn die Klassen, eine Methoden deklariert, nur um eine Schnittstelle implementieren zu können, sollten in Erwägung gezogen werden, diese Schnittstelle in zwei oder sogar mehr zu zerlegen. Auf diese Weise bleiben die Vorteile erhalten und das Ergebnis ist ein viel saubereren Code, der den SOLID-Prinzipien folgt.
D -> Dependency Inversion Principle
Das D steht für Dependency Inversion Principle oder kurz DIP. Die Begriffe Dependency Injection (DIP) und Inversion of Control (IoC) werden im Allgemeinen als austauschbar verwendet, um dasselbe Entwurfsmuster auszudrücken. Inversion of Control (IoC) ist eine Technik zur Implementierung des Dependency Inversion Principle, die 2004 von Martin Fowler beschrieben wurde. Die Inversion der Kontrolle kann je nach Sprache entweder mit einer abstrakten Klasse oder einer Schnittstelle implementiert werden. Kurz gesagt löst DIP das Problem der engen Kopplung von Klassen untereinander, das sich in zwei formalen Definitionen zusammenfassen lässt:
High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beide sollten von Abstraktionen abhängen.
Eine konkrete Klasse, die von einer anderen abhängt, wird wahrscheinlich zu Problemen führen, während sich der Code weiterentwickelt. Abstrakte Klassen ändern sich in der Regel nicht. Da diese Art von Klasse ist die einzige mit Abhängigkeiten sein sollte, wird dieser Umstand die Wartbarkeit erhöhen und auf Verhaltensänderungen besser vorbereitet sein.
Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen.
Die größten Vorteile dieses Prinzips treten in zwei Situationen auf:
- Wenn Änderungen an konkreten Implementierungen auf Low-Level-Modulen vorgenommen werden und es keine Änderungen an der Abstraktion gibt, die zwischen den Low-Level- und High-Level-Modulen besteht;
- Wenn ein neues Low-Level-Modul erstellt wird und es mit dem bestehenden Code unter Verwendung der etablierten Abstraktion interagieren kann.
In beiden Fällen bleiben die High-Level-Module unverändert, wodurch der Code besser wartbar und erweiterbar ist.