Start

Meine ganz persönliche SichtDie SOLID Prinzipien der OOP

Als Programmierer von objekt-orientierten Programmen sollte man unbedingt die SOLID Prinzipien kennen. Diese sind schon häufig ausführlich beschrieben worden. In diesem Artikel beschreibe ich meine ganz persönliche Sicht darauf und welche Erfahrungen ich damit gemacht habe.

Solide im wahrsten Sinne

Soweit es mir bekannt ist, wurden die SOLID Prinzipien Anfang 2000 zusammengestellt aus verschiedenen Veröffentlichungen von Robert C. Martin. Sie waren eine Sammlung von Best Practices für die Entwicklung von objekt-orientierten Programmen.

Seitdem hat sich viel getan in der Software-Entwicklung. Neue und schnellere Hardware wurde erfunden, neue Programmiersprachen erschaffen und immer mehr Menschen entwickeln Software. Aber was ist geblieben?

  • Auf Version 1 folgt Version 2. Sobald ein Programm fertig entwickelt ist, kommt schon die nächste Anforderung, die umgesetzt werden muss. Hoffentlich passt das alles gut zusammen.
  • Es muss schnell gehen. Auftraggeber sind immer noch ungeduldig und neue Funktionen verkaufen sich nun mal am Besten. Für Refactoring ist selten Verstädndnis im Management.
  • Software wird häufiger gelesen als geschrieben. Wird eine Software in einem Unternehmen entwickelt, verdingen sich über die Jahre eine ganze Reihe von verschiedenen Programmierern daran. Nicht selten sind die ursprünglichen Entwickler nach einiger Zeit nicht mehr im Unternehmen und neue Leute müssen den Code von anderen verstehen, übernehmen und erweitern.
  • Code steht nicht in einer einzigen Datei. Der Umfang von Software kann ganz schnell beträchtlich werden und früher wie heute wird der Code auf viele Dateien und Module verteilt. Durch unachtsam gewählte Abhängigkeiten kann ein Software-Projekt ganz leicht unwartbar werden.
  • Die SOLID Prinzipien sind immer noch aktuell. Das ist das Erstaunliche! Obwohl sich so viel in der OO-Welt getan hat; die SOLID Prinzipien sind wirklich grundsolide und erweisen sich so nützlich und sinnvoll wie eh und je.

Meine Sicht auf die SOLID Prinzipien

Im Folgenden werde ich auf die einzelnen Prinzipien eingehen, aber sie nicht erklären, sondern meine ganz persönliche Sicht und Erfahrungen mit ihnen darlegen. Es würde mich freuen, von anderen Entwicklern zu hören, ob sie meine Sicht teilen und ähnliche Erfahrungen gemacht haben oder ob sie es gänzlich anders sehen.

Vielleicht werde ich Programmierer. Geistige Arbeit ist nicht so anstrengend.

Single Responsibility Principle

Es sollte nie mehr als einen Grund dafür geben, eine Klasse zu ändern.

– Robert C. Martin: Agile Software Development: Principles, Patterns, and Practices

Wer noch unerfahren in der OOP ist, wird mit diesem Prinzip nichts anfangen können. Ich wage zu behaupten, dass so ziemlich jeder am Anfang seiner Programmierer-Karriere nur ein Ziel hatte: Das Programm muss funktionieren! Alles andere ist sekundär.

Mit jedem Software-Projekt wächst die Erfahrung, vor allem wenn gestandene Entwickler mit von der Partie sind, von denen man lernen kann und das auch möchte. Man trifft dann unweigerlich auf große Klassen, deren Code umfangreich und oftmals nur schwer verständlich ist. Die normale Reaktion: "Wer schreibt denn so einen Haufen Sch...?"

Die paradoxe Erfahrung, die ich mit dem Single Responsibility Prinzip gemacht habe:

Wenn andere große Klassen schreiben, regt man sich darüber auf. Wenn es die eigenen Klassen sind, ist das ok.

Aber ganz im Ernst: in praktisch jedem Projekt, in dem ich bislang gearbeitet habe, wird früher oder später dieses Prinzip verletzt. Es entstehen sogenannte Gott-Klassen, die nicht nur eine Aufgabe haben, sondern viele grundlegend verschiedene Sachen erledigen. Und damit haben sie mehr als einen Grund, sich zu ändern.

Zusammenfassung

  • Unerfahrenen Programmierern ist dieses Prinzip egal — Hauptsache dat Ding läuft.
  • Erfahrenere Programmierer kennen das Prinzip, wenden es auch gelegentlich an eigenem Code an, ignorieren es geflissentlich (wenn's mal schnell gehen muss) und monieren es gerne bei anderer Leute Code. So einen richtigen Schmerz spüren sie aber noch nicht dabei.
  • Richtig erfahrene Programmierer erkennen Gott-Klassen als ernsthaftes Problem, das Bauchschmerzen verursacht. Sie produzieren zwar manchmal vorübergehend Klassen, die mehr als einen Grund zur Änderung haben, aber sie drängen darauf, dies beim nächsten Refactoring aufzulösen.

Open-Closed Prinzip

Module sollten sowohl offen (für Erweiterungen), als auch geschlossen (für Modifikationen) sein.

– Bertrand Meyer: Object Oriented Software Construction

Meine Erfahrung mit diesem Prinzip in der Praxis ist, dass es fast nie angewendet wird. Man findet fast immer die Situation vor, dass bei Erweiterungen nicht etwa nur neue Klassen implementiert werden, sondern vor allem Änderungen an bestehenden Klassen.

Warum ist das ein Problem?

Ich halte das aus mehreren Gründen für problematisch:

  • Erhöhte Kosten, weil getesteter Code verändert wird. Nehmen wir an, wir haben eine Version 1.0 einer Software in Produktion, in der alles getestet ist. Werden jetzt für Version 2.0 neue Funktionen implementiert, kommt logischerweise neuer Code hinzu, der getestet werden muss. Wenn dieser neue Code in vorhandene Klassen kommt, riskiert man damit, funktionierenden Code kaputt zu machen. Auch vorhandene Tests für diesen Code können dabei anzupassen sein.
  • Schwerer zu verstehen. Source Code, der das Open-Close Prinzip umsetzt, ist in der Regel auch sehr gut zu verstehen, da die Rollen und Aufgaben klar auf einzelne Klassen verteilt sind. Wenn ich z.B. schon eine TextProcessor Klasse und eine JpgProcessor Klasse habe, ist es einfach eine PngProcessor Klasse hinzuzufügen. Wenn alle diese Prozessoren in einer Klasse wären, wäre die Sache viel unübersichtlicher.
  • Einfacher zu testen. In Tests benötigt man häufig Mocks, um seine Tests einfach zu erstellen. Für Source Code, der das Open-Close Prinzip beherzigt, ist es meisten sehr einfach Mocks zu erstellen.

Zusammenfassung

  • Das Open-Close Prinzip ist ein seltener Gast in Software-Projekten. Dies ist meine persönliche Bilanz; ich würde mich freuen, wenn mich meine persönliche Erfahrung täuschen würde.
  • Das Open-Close Prinzip sollte von viel mehr Programmierern verstanden und beherzigt werden. Daher sollten erfahrenere Programmierer in Code Reviews mehr auf dieses Prinzip achten.
  • Meine Empfehlung: Frühe Design-Reviews. In Projekten gibt man Programmierern gerne eine AUfgabe und wenn sie fertig sind, gibt es ein Code Review. Wenn dort das verletzte Open-Close Prinzip moniert wird, ist die Standardantwort, dass das zwar eine gute Idee wäre, aber jetzt ist es ja schon fertig programmiert und für ein Refactoring ist keine Zeit mehr. Daher empfehle ich, dass der Programmierer sich ein Design zur Umsetzung überlegen soll und bevor er etwas umsetzt, diskutiert man das im Kollegenkreis. Ich habe es noch nie erlebt, dass man in einer solchen Diskussion mit einer schlechteren Lösung herausgegangen ist. Dies sorgt auch bei allen für eine bessere Stimmung und mehr Motivation.

Liskov Substitution Prinzip

Wenn S ein Subtyp von T ist, dann können Objekte des Typs T durch Objekte des Typs S ersetzt werden, ohne dass sich die gewünschten Eigenschaften des Programms ändern.

– Barbara H. Liskov, Jeannette M. Wing: Behavioral Subtyping Using Invariants and Constraints Construction

Wenn man Programmierer fragt, ob sie das Liskov Substitution Prinzip beherzigen, bekommt man meistens große Augen zu sehen. Das liegt wohl daran, dass dieses Prinzip sehr mathematisch formuliert ist und viele die Definition nicht wirklich verstehen. Dabei drückt das Prinzip doch nur eine grundlegende Eigenschaft von OOP aus. Das bedeutet, dass man jede Unterklasse anstelle ihrer Elternklasse verwenden kann ohne das Verhalten des Programms zu verändern.

Ich habe festgestellt, dass die meisten Programmierer das intuitiv richtig machen. Es passiert relativ selten, dass man auf eine Subklasse trifft, die ein gänzlich anderes Verhalten als das per Contract vereinbarte an den Tag legt. Wenn das passiert, ist die Überraschung auf Seiten des Aufrufers groß.

Vielleicht kann man das Liskov Substitution Prinzip flapsig so formulieren:

Sorge dafür, dass abgeleitete Klassen Verträge / Promises einhalten, um den Aufrufer nicht zu überraschen.

Interface Segregation Prinzip

Clients sollten nicht dazu gezwungen werden, von Interfaces abzuhängen, die sie nicht verwenden.

– Robert C. Martin: The Interface Segregation Principle

Die Verletzung dieses Prinzips findet man sehr häufig in der freien Wildbahn. Statt einem Client nur das zu überreichen, was er tatsächlich benötigt, wird ganz häufig die gesamte Klasse direkt übergeben.

Das ist in etwa so, wie wenn man im Supermarkt an der Kasse €13,95 bezahlen soll und statt dem Mitarbeitenden an der Kasse einen passenden Geldschein zu überreichen, gibt man seine Aktentasche mit Portemonnaie und Geschäftsunterlagen weiter in der Hoffnung, dass schon das richtige herausgenommen wird. Was im realen Leben nicht passiert, wird im übertragenen Sinn beim Programmieren sehr häufig praktiziert.

Warum ist das ein Problem?

Man könnte ja argumentieren, dass das unproblematisch sei, weil Hauptsache es ist genügend Information da. Etwas mehr Information kann ja nicht schaden. Was in der Realität früher oder später passiert, ist, dass dieses Mehr an Möglichkeiten auch genutzt wird (ist ja so praktisch) und dadurch entstehen zusätzliche Abhängigkeiten, die fortwährend unterstützt werden müssen. Nur in den seltensten Fällen ist diese Abhängigkeit auch so gewollt. Und wie wir alle schon schmerzhaft erfahren haben: Abhängigkeiten können Software-Projekt zum Erliegen bringen!

Dependency Inversion Prinzip

Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen.

– Robert C. Martin: The Dependency Inversion Principle

Mit der guten Unterstützung von DI-Containern in .NET findet man das Dependency Inversion Prinzip häufig im Einsatz. In der Praxis habe ich es erlebt, dass Container nur dann verwendet werden, wenn der Constructor eines Objekts nur von anderen ebenfalls im Container befindlichen Objekten abhängt. Sobald Parameter dazu kommen, die erst zur Laufzeit bekannt sind, wird gerne mal auf Container verzichtet, statt mit entsprechenden Factories zu arbeiten.

Das arbeiten mit DI-Containern vereinfacht das Entwickeln so ungemein, dass man darauf auch nicht verzichten möchte. Aufwändige Objektgraphen können viel leichter erzeugt werden und auch das Testen wird massiv vereinfacht, weil man sich gemockte Objekte in den Container legen kann.

Firma

di - IT Consulting
Dirk Illenberger
Bismarckstr. 24
61169 Friedberg

© 2021 di - IT Consulting, Dirk Illenberger