Clean Code
Kommentare

SOLID-Prinzip: Liskov Substitution Principle (LSP)
Das nächste Prinzip schließt nahtlos an. Barbara Liskov hat in einem Artikel [9] gefordert, dass Subklassen ihre Superklassen immer ersetzen können.

SOLID-Prinzip: Liskov Substitution Principle (LSP)

Das nächste Prinzip schließt nahtlos an. Barbara Liskov hat in einem Artikel [9] gefordert, dass Subklassen ihre Superklassen immer ersetzen können. Was zunächst als Selbstverständlichkeit klingt, ist ein oft missachtetes Prinzip: Wenn eine Methode m einer Klasse einen Typ A (zum Beispiel als Parameter) erwartet, um ihre Verarbeitung zu machen, dann wird damit erwartet, dass diese Methode m auch auf Subtypen von A unverändert reagiert. Vielmehr noch sollte m nichts über die möglichen Subtypen wissen und schon gar nicht in speziellen Fällen „zerbrechen“.

Dahinter steckt in Wahrheit ein viel tieferes Problem: Abgeleitete Typen („Subtypen“) verändern das Verhalten einer Klasse (Robert Martin nennt dies „IS-A is about behavior“ [8] und eine Methode m, von der wir soeben gesprochen haben, trifft Annahmen über das Verhalten der von ihr verarbeiteten Typen. Wird diese aber häufig gebrochen, dann sprechen wir von einer Verletzung dieses Prinzips. Ein einfaches Beispiel ist die Berechnung der Summenfläche in einer Liste von geometrischen Figuren. Die Methode m würde hierzu eine Funktion getArea()über alle Elemente der Liste aufrufen und ggf. eine Ausnahme (Exception) erwarten, wenn etwas schief läuft. Würde ein neuer Subtyp zum Beispiel nun eine neue Exception werfen, so würde die Funktion fehlschlagen und das Prinzip hiermit verletzt werden.

Es geht also um die Annahmen, die ein Entwickler über die Verwendung seiner Klasse trifft, wenn er sie entwirft – „Designentscheidungen“, die auch für die zukünftige Verwendung einer Klasse oder Bibliothek wichtig sind. Da wir jedoch nicht zu viel antizipieren wollen und können, macht es hier mehr Sinn, die Designentscheidungen und damit verbundenen Rahmenbedingungen für deren Verwendung zu dokumentieren. Es gibt eine Technik, die solche Annahmen für Clients einer Klasse oder Methode explizit macht und damit das LSP im Grunde erzwingt: „Design by Contract“ (DBC), eine von Bertrand Meyer [4] beschriebene Technik, bei der jede Klasse den „Vertrag“ explizit in Form von Vor- und Nachbedingungen für jede Methode festschreibt. In der Zeit vor Unit Tests wurden dazu die „Assert“-Funktionen einer objektorientierten Programmiersprache verwendet. Wenige Sprachen, wie zum Beispiel das von Bertrand Meyer selbst entwickelte „Eiffel“, hatten auch eingebaute Unterstützung für Pre- und Post-Conditions in der Laufzeitumgebung. Diese Art hatte sich, zumindest der Meinung der Autoren dieses Buches nach, nicht besonders durchgesetzt.

Unit Tests sind hingegen eine hervorragende Art und Weise, die Verwendung und die Annahmen, Einschränkungen sowie Vor- und Nachbedingungen einer Klasse klarzustellen. Die korrekte Verwendung, im Zuge von TDD zum Beispiel, ist ein gleichwertiges Instrument, um das Liskov-Prinzip zu erzwingen und die Designentscheidungen mittels Test Fixtures zu dokumentieren.

SOLID-Prinzip: Interface Segregation Principle (ISP)

Bei diesem Prinzip geht es um zu „fette“ Schnittstellen, die manchmal im Zuge von wachsenden Anwendungen entstehen. Konkret ist es ein Problem, wenn Clients gezwungen werden, von Methoden einer Schnittstelle abzuhängen, die sie nicht benötigen. Das erkennt man dann, wenn die Schnittstelle so lang wird, dass sich „Gruppen von Methoden“ bilden, die offensichtlich zusammen gehören. In der Regel ist es dann auch so, dass einige Klienten die eine Gruppe von Methoden benutzt und andere Methodengruppen von anderen, meist verschiedenen Klienten verwendet werden.

Diese fetten Schnittstellen erzeugen unnötig hohe Kopplung zwischen den Klienten, also den Klassen, die diese verwenden. Wenn ein Klient eine Änderung bei der „fetten“ Klasse verursacht, sind in der Regel alle anderen Klienten ebenso betroffen. Das Prinzip sagt somit auch aus, dass Klienten nur von den Methoden abhängen sollen, die sie auch verwenden.

Diese „fetten“ Klassen weisen eine geringe Kohäsion auf. In vielen Fällen reicht es aus, dass die Schnittstellen, und damit die Klassen, auf mehrere aufgeteilt werden. Manchmal jedoch sind Objekte mit nichtkohäsiven Schnittstellen notwendig – eine saubere Schnittstelle muss dann mit anderen notwendigen Methoden „verschmutzt“ werden, zum Beispiel, wenn eine Klasse persistent gemacht werden muss oder eine weiter abgeleitete Klasse eine bestimmte abstrakte Basis einfordert. In der klassischen objektorientierten Programmierung wurde hier dann häufig die Mehrfachvererbung verwendet.

In der Regel kann Mehrfachvererbung auch mittels „Delegation“ ersetzt werden, sodass Zweck und Sauberkeit in gleichem Maße bedient werden können. Bei der Delegation handelt es sich um ein weiteres Objekt, das die zum Zweck der abgeleiteten Klasse notwendige Basis implementiert (zum Beispiel die Persistenz), jedoch nicht Teil der Schnittstelle ist. Wir lagern also den oben als „verschmutzend“ beschriebenen Teil der Schnittstelle in eine andere Klasse aus, die wir zur Laufzeit erzeugen und in unserer abgeleiteten Klasse zu diesem Zweck verwenden. Dieses Verwendungsmuster ist auch als „Adapter“ bekannt [10].

SOLID-Prinzip: Dependency Inversion Principle (DIP)

Um die Abhängigkeit von Klassen untereinander geht es auch beim letzten der fünf SOLID-Prinzipien. Objektorientierte Architekturen sollten ja klar definierte Schichten mit kohärenten Diensten anbieten. Sehr häufig kommt es dann dazu, dass wir Schichten mit unterschiedlich konkreten oder an der Geschäftslogik fokussierten Funktionen haben, bei denen die Klassen von höherwertigen Schichten von Klassen aus den unteren Schichten abhängig sind (Abb. 1).

Abb. 1: Schichten in einer Architektur, nicht konform zu DIP

Auf den ersten Blick mag das angemessen wirken, bei genauerer Betrachtung sieht man, dass eine Klasse in der Businesslogik-Schicht anfällig für jegliche Änderungen ist, die entlang des Weges bis runter zur Schicht der „allgemeinen Dienste“ (Utility) passieren, was klarerweise nicht gut ist, da damit wieder einmal eine hohe Kopplung zwischen den Klassen dieser Schichten geschaffen wird.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -