8 DINGE, DIE ICH ÜBER OBJEKTORIENTIERTES DESIGN GELERNT HABE (PART 2)

Im vorherigen Beitrag wurden schon 4 Konzepte vorgestellt, die man in der praktischen objektorientierten Softwareentwicklung selten findet. Es folgen nun 4 weitere.
Inhalt
5. Objekte kommunizieren über Nachrichten
Auch wenn Objekte in der Objektorientierung zentral scheinen, teilen sie sich diesen Ruhm mit Nachrichten. Und wenn man ein objektorientiertes Design vom Standpunkt der Kommunikation von Objekten aus betrachtet, können weniger offensichtlichere, aber genauso notwendige Objekte besser aufgedeckt werden.
Konzentriert man sich auf die Objekte an sich, tendiert man eher dazu, Verantwortlichkeiten in Objekten zu sehen, die besser anderswo aufgehoben wären. Als Anfänger der objektorientierten Programmierung beginnt man fast immer damit, UML-Klassendiagramme zu erstellen, um die benötigten Verhaltensweisen Objekten zuzuordnen.
Man ist jedoch besser damit beraten, mit Sequenzdiagrammen zu arbeiten, um den Kommunikationsfluss aufzudecken, der für eine bestimmte Aufgabe notwendig ist. Das hilft besser dabei, verstecktes oder nicht offensichtliches Wissen aufzudecken, dass Objekte eventuell „zu viel“ haben.
6. Objekte dürfen erben
„Vererbung ist böse“, heißt es immer. Aber: böse ist Vererbung nur dann, wenn sie fehlplatziert oder falsch eingesetzt wird. Folgende Vererbungshierarchie bringt höchstwahrscheinlich mehr Probleme mit sich, als sie löst:

Das sogenannte „Problem der zerbrechlichen Superklasse“ (fragile base class problem) ist offensichtlich: ändert sich eine Superklasse, wird die mit Vererbung verbundene Kopplung dazu führen, dass sich Subklassen ebenfalls ändern müssen, oder nicht mehr funktionieren.
Das hier ist eine Vererbungshierarchie, die weitaus weniger Risiken birgt:

Natürlich besteht auch hier eine Kopplung zwischen der Superklasse und den Subklassen, aber das Risiko ist deutlich geringer. Hier wird eine flache Vererbungshierarchie eingesetzt, anstatt die pyramidenförmige weiter oben. Solch eine Vererbungshierarchie leidet immer noch am „fragile base class“ Problem, aber es gibt nur eine fragile Superklasse, nicht mehrere. Die Kopplung zwischen Super- und Subklasse kann zusätzlich noch durch den Einsatz von Template Methods reduziert werden.
Ein weiterer Fehler, der bei der Modellierung von Vererbungshierarchien gemacht wird, ist Hierarchien aufgrund von gemeinsamen Eigenschaften aufzubauen. Korrekterweise müssen Super- und Subklassen jedoch ein gemeinsames Verhalten an den Tag legen, was nur in ganz klar definierten Aspekten spezialisiert wird. Das Liskov´sche Substitutionsprinzip kann nicht gewährleistet werden, wenn Vererbungshierarchien auf gemeinsamen Attributen aufgebaut werden und man letztlich herausfindet, dass Klasse X eben nicht wirklich auch Klasse Y ist.
Nichtsdestotrotz: Vererbung ist ein Designmittel, dessen Einsatz wohlüberlegt sein sollte, und für das es in den allermeisten Fällen gute, risikoärmere Alternativen gibt. Aber „böse“ ist nur der unbedachte Einsatz von tiefen Vererbungshierarchien.
7. Manchmal ist es einfach kein Objekt, …
… sondern ein Service. In der Praxis werden Services gerne und häufig eingesetzt, mit generischen Namen wie zB. AgbService. In der ursprünglichen Fassung von „Domain-Driven Design: Tackling Complexity in the Heart of Software“ von Eric Evans sind Domänenservices allerdings nur dann einzusetzen, wenn ein bestimmtes Verhalten oder eine bestimmte Operation nicht „auf natürliche Art und Weise“ einem Domänenobjekt zugeordnet werden kann, bzw. dessen Rolle im Domänenkontext verschleiern würde.
Der weitaus häufigere Fehler ist es laut Evans jedoch, zu früh aufzugeben, ein benötigtes Verhalten einem Domänenobjekt zuzuordnen, und durch Services immer weiter in die prozedurale Programmierung abzurutschen.
Was meint er damit?
Wie oben bereits gezeigt, sollte objektorientiertes Design das benötigte Verhalten auf Objekte verteilen, anstatt den Kontrollfluss zu zentralisieren.
Wenn nun aber das Verhalten größtenteils in Services ausgelagert wird, statt auf Domänenobjekte zu verteilen, schaffen wir über die Zeit einen zentralen Kontrollfluss, weil die Services letztlich mehr und mehr Verantwortlichkeiten übernehmen.
Domänengetriebenes Design setzt auf Prinzipien der Objektorientierung auf, und negiert sie nicht.
Domänenservices sollten daher sparsam eingesetzt werden und sie sollten nur die Aufgabe übernehmen, die sonst nirgendwo reinpasst. Auf diese Weise verhindert man zumindest generische und nichtssagende „Handler“ oder „Manager“ Objekte im Domänencode, die keine wirkliche Parallele im Domänenkontext haben.
8. Objekte sind Denkwerkzeuge, nicht Programmierwerkzeuge
Objektorientierte Programmierung sah sich in der jüngeren Vergangenheit viel Kritik ausgesetzt. Andere, neue Konzepte wurden als „besser“ angepriesen, und der „Tod“ objektorientierter Programmierung vorausgesagt. Doch die religiös geführten Diskussionen um die „bessere“ Programmiersprache oder das „bessere“ Paradigma übersehen: es ist in jeder Programmiersprache möglich, schlechte Software zu produzieren. Es ist auch in jeder Programmiersprache möglich, gute Software zu produzieren. Das Problem sind nicht Programmiersprachen oder Paradigmen, sondern die Art und Weise, wie Konzepte und Paradigmen beim Programmieren umgesetzt werden.
Objektorientierung beinhaltet eine Vielzahl von Konzepten und wurde über die Jahre weiterentwickelt. Sind diese Konzepte unbekannt, fällt man trotz OO-Sprache allzu leicht auf die prozedurale Programmierung zurück.
Und selbst wenn ein objektorientiertes Design konsequent verwendet wird, führt auch das nicht zwangsläufig zu einem flexiblen und verständlichen Softwaresystem, denn alle objektorientierten Designmittel können falsch angewendet, fehl platziert oder schlichtweg übertrieben werden. Auch dann kann Objektorientierung seinen Ansprüchen nicht gerecht werden.
Der Beitrag ist bewusst auf einer hohen Flugebene geblieben. Zukünftige Beiträge werden sich den hier genannten Aspekten genauer und mit praktischem Bezug widmen. Dennoch fällt eines auf: Praxis und Theorie der objektorientierten Programmierung sind aktuell so weit auseinander, dass dessen eigentliche Stärken nicht zur Geltung kommen können.
