Mittwoch, 30. Januar 2013

Das Dependency Inversion Prinzip

Wie kaum ein anderes Prinzip, hat das Dependency Inversion Prinzip in den nunmehr fast 20 Jahren seit seinen ersten Anfängen einen Siegeszug durch die Softwaretechnik angetreten, und das auch ohne von seinen Anwendern in der letzten Tiefe verstanden worden zu sein. Die Ursache hierfür liegt insbesondere in der Verbreitung von Dependency Injection Containern wie Spring, welche den Entwickler fast automatisch zu einem Entwurfsstil führen, der dem DIP folgt. Die Liste der hier behandelten S.O.L.I.D.-Prinzipien ist somit komplett.

Name, Kurzform Dependency Inversion Prinzip
Synonyme
Umkehr der Abhängigkeiten,
DIP
Beschreibung
Robert C. Martin formulierte dieses Prinzip in einer frühen Fassung [Martin 1996] zunächst in insgesamt vier Sätzen, die beim ersten Lesen wenig eingängig erscheinen:
A. Module hoher Ebenen sollten nicht von Modulen niedriger Ebenen abhängen. Beide sollten von Interfaces abhängen. B. Interfaces sollten nicht von Details abhängen. Details sollten von Interfaces abhängen.
Dabei spricht Martin in der Version von 1996 noch nicht von „Interfaces“, sondern von „Abstraktionen“ (was der Fokussierung auf C++ geschuldet ist). Eine kürzere Fassung liefert Martin heute in seinem Blog:
Erlaube Abhängigkeiten von Abstraktionen, nicht von Spezialisierungen.
Erläuterung
Trotz der umständlicheren Formulierung lohnt ein Blick auf die ursprüngliche Version des Prinzips: Das zu Grunde liegende Bild ist eine Modul-Hierarchie: Module (höherer Ebenen) enthalten Module (niedrigerer Ebenen) und delegieren einen Teil der Arbeit an diese. Das zu Grunde liegende Konstruktionsprinzip ist „Teile und herrsche“. Komplexe Software entsteht durch die Komposition kleinerer Module zu immer komplexeren Modulen.
Martin fordert nun, dass das in der Hierarchie höherwertige Modul nur die Abstraktion (das Interface) seiner Dienstmodule kennen darf und nicht die Dienstmodule selbst.
Stellt man sich eine tiefe Modul-Hierarchie vor, so könnten sich Änderungen auf einer tiefen Modul-Ebene bis in die höchsten Ebenen auswirken, wenn diese Bedingung nicht erfüllt würde. Umgekehrt wäre es nicht möglich, höhere Ebenen ohne sämtliche Implementierungs-Details niederer Dienstmodule zu anderen Zwecken wiederzuverwenden, sei es auch nur für Testzwecke.
Genau dies wird durch Abstraktion erreicht: Zwischen höherwertigen Modulen und niederwertigen wird eine Schicht eingezogen. Die Delegation erfolgt nicht an einen konkreten Dienstleister, sondern an diese Abstraktionsschicht. Hierachisch aufgebaute Systeme werden auf diese Weise systematisch von Implementierungs-Details entkoppelt. Details werden austauschbar.
Beispiele
  • Die Bildung von Schichten zur Entkoppelung von Details ist nicht neu. Im OSI-Schichtenmodell stellt jede nächstniedere Schicht für die ihr übergeordnete Schicht eine Abstraktion dar. Die Details der tieferen Schichten werden entkoppelt.
  • Das Entwurfsmuster Adapter ist eine Anwendung der Dependency Inversion.
  • Dependency Injection ist der Mechanismus, der Dependency Inversion in der Praxis besonders leicht anwendbar macht: Durch Konstruktor-Injection, Dependency Lookup oder (am besten) einen Dependency Injection Container werden die niederwertigen Module in die höherwertigen injiziert, ohne dass diese Kenntnis der Spezialisierungen benötigen. Durch Dependency Injection Container ist Dependency Inversion zu einem Allgemeingut geworden.
Historie
  • Der Begriff Dependency Inversion taucht erstmals in [Martin 1994] auf, ohne dort bereits in die prägnante Form eines Prinzips gebracht zu werden.
  • Ausarbeitung in Prinzipien-Form in [Martin 1996].
  • Populär geworden durch das Buch [Martin 2002].
Art des Prinzips
  • Grundlegende Einteilung: Produktprinzip.
  • Technologiebezug: Allgemeingültig.
  • Entwurfsgüte: Strukturprinzip.
  • Handlungsbezug: Erleichterung von Änderung, Wiederverwendung und Verstehen.
  • Kognitionsbezug: Komplexitätsreduktion.
(Siehe Kategorisierung der Prinzipien.)
Grad der formalen Spezifikation Hoch, da syntaktisch überprüfbar.
Vorteile
  • In Kombination mit Dependency Injection Containern sehr einfach anwendbar.
  • Sehr universell, da auf praktisch allen Strukturierungs-Ebenen gültig. Dadurch breiter Anwendungsbereich.
Nachteile
Die Nachteile der Abstraktion:
  • erhöhter Entwurfs- und Umsetzungsaufwand
  • weniger konkret, dadurch weniger unmittelbar verständlich
Übergeordnete Prinzipien
  • Prinzip der losen Koppelung
  • Prinzip der Abstraktion
  • Hierarchisierungsprinzip
  • Teile und herrsche
Abgleitete Prinzipien -
Qualitätsmerkmale Änderbarkeit (+), Testbarkeit (+), Wiederverwendbarkeit (+), Verständlichkeit (-), Einfachheit (-)

Quellen:

[Martin 1994] – OO Design Quality Metrics - An analysis of dependencies, Robert C. Martin (1994), hier als PDF-Dokument verfügbar 
[Martin 1996] – The Dependency Inversion Principle, Robert C. Martin, C++ Report (1996)
[Martin 2002] – Agile Software Development. Principles, Patterns, and Practices, Robert C. Martin (2002)