Dependency Inversion – ganz neu entdeckt

Veröffentlicht: 7. Juli 2011 von pmischke in Dependency Injection

So, jetzt geht’s los! Dies ist der erste Post im Themenfeld „Dependency Injection“. Ich hoffe, es gelingt mir, dieses hoch interessante Thema weiter zu verfolgen.

Am Anfang steht das „Dependency Inversion Principle“ (DIP) von Robert Martin. Genauer gesagt war es 1995, was aus meiner beruflichen Perspektive schon sehr weit am Anfang ist. Trotzdem sieht es nicht so aus, als ob dieses Prinzip in der Software-Entwicklung allgemein verinnerlicht ist. Mir geht es nicht anders. Lange Zeit hatte ich nur ein sehr oberflächliches Verständnis des DIP. Seit kurzem bin ich einen Schritt weiter.

Aber was ist das DIP eigentlich? Es gibt viel Material dazu im Netz. Eine umfangreiche und verständliche Erklärung findet Ihr zum Beispiel hier:

Wir lernen also,

A. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.

Ich finde es ganz interessant, sich zunächst einmal zu vergegenwärtigen, warum wir diese Abhängigkeiten von oben nach unten überhaupt haben. Martin weist im Original Artikel darauf hin. Die traditionelle strukturierte Programmierung lehrt uns, große Probleme in mehrere kleine Probleme aufzuteilen und dann die große Lösung aus den kleinen Lösungen zusammen zu setzen. So ergeben sich die Hierarchien, in denen das oberste Modul letzendlich von allen anderen abhängt. Jede Änderung wirkt sich die Hierarchie hoch durch das ganze System. Eigentlich ziemlich unschön, aber dieses Prinzip sitzt sehr fest in unseren Köpfen. Die objektorientierte Programmierung hat bisher nur wenig daran geändert.

Obwohl der objektorientierte Werkzeugkasten mehr zu bieten hat. Das DIP zeigt, dass die Abhängigkeiten von oben nach unten durch Abstraktionen umgekehrt werden können. Für die Abstraktionen nehmen wir Schnittstellen, also Interfaces in Java und C#. Weiter heißt es im DIP

B. Abstractions should not depend upon details. Details should depend upon abstractions.

Was bedeute das? Dieser seltsam wirkende zweite Teil hat etwas mit C++ zu tun. Ja genau, um 1995 hab auch ich mit C++ entwickelt, aber das ist Geschichte. Mit den Interfaces aus Java kann ich meine Module perfekt entkoppeln, ohne mir darüber Gedanken zu machen.

Mit diesem Erkenntnisstand bin ich lange gut gefahren. Zumindest dachte ich das. Bei dem Versuch, meine Kollegen vom DIP und dem guten Design zu überzeugen, welches sich damit erreichen lässt, kam dann folgender Einwand. Die Schnittstelle macht nur mehr Arbeit. Änderungen fanden vorher nur in den beiden Module statt. Und nun muss ich immer drei Stellen ändern. Die Schnittstelle, das implementierende Modul und das nutzende Modul. Ja das stimmt zwar… aber nur wenn sich die Schnittstelle ändert. Ich kann also leicht meine Bugs in der Implementierung fixen. Bei wirklichen Änderungen habe ich zugegebenermaßen nicht viel gewonnen. Tja, dagegen kann man nichts machen.

Dumm nur, das unser Geschäft quasi ausschließlich aus Änderungen besteht. Und eigentlich kann ich Bugs auch gut ohne separierte Schnittstelle in dem Modul fixen, wenn die Signaturen der Methoden nicht betroffen sind. Was bringt mir das DIP also?

Der Schlüssel liegt in dem zweiten Teil des DIP, den ich so schnell beiseite gelegt hatte. Abstraktionen dürfen nicht von Details abhängen! Anders ausgedrückt, die Schnittstelle muss überhaupt eine Abstraktion sein. In der IDE schnell mal den „Extract Interface“ Wizzard starten, erzeugt zwar eine Schnittstelle, aber keine Abstraktion. Diese Schnittstellen unterscheiden sich nicht einen Deut von den öffentlichen Methoden eines Moduls, das ohne Schnittstelle genutzt wird. Es stellt sich genau der Effekt ein, dass alle Änderungen trotz Schnittstelle nach oben durchschlagen.

Ich möchte einfaches Beispiel geben.

public Customer findCustomerById(long id);

Diese Abstraktion hängt von Details ab. Nämlich von dem Detail, dass ich den long Typ für meine IDs verwende. Bei der Umstellung auf UUID wird wahrscheinlich einiges an Code geändert werden.

public Customer findCustomerById(ID id);

Diese Abstraktion hängt nicht von Details ab. Wenn mein eigener Typ ID den Typ der Implementierung versteckt, ist das nutzende Modul von einer Umstellung entkoppelt. Wenn sich das Wesen der Methode nicht ändert, wird sich auch die Schnittstelle nicht ändern. Eine Methode, die einen Kunden zu einer gegebenen ID findet, wird immer eine ID als Parameter bekommen und einen Kunden zurück liefern. Egal, wie die Implementierung aussieht.

Es ist nicht einfach, die echte Abstraktion zu finden, die hinter einer Schnittstelle liegt. Die IDE kann das nicht leisten. Hier ist das Gehirn gefragt. Und nicht zu knapp. Aber es lohnt sich. Die echte Abstraktion bringt Stabilität in die Schnittstelle. Häufige Änderungen an der Schnittstelle sind ein Hinweis auf mangelnde Abstraktion. Der zweite Teil des DIP ist für mich inzwischen wichtiger geworden, als der erste. Ohne Abstraktion funktioniert die Schnittstelle nicht.

Advertisements
Kommentare
  1. […] folgenden möchte ich gerne den Übergang vom letzten Eintrag Dependency Inversion Principal (DIP) zum Dependency Injection (DI) Container schaffen. Mit dem Titel meines Posts beziehe ich mich […]

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s