Anmeldung Registrierung
Auto Hell Dunkel
Erweiterte Suche
  1. Startseite
  2. Podcasts
  3. IT-Berufe-Podcast Podcast
  4. Dependency Inversion Principle (DIP) – Wissenshäppchen #7
Dependency Inversion Principle (DIP) – Wissenshäppchen #7

Dependency Inversion Principle (DIP) – Wissenshäppchen #7

IT-Berufe-Podcast vor 7 Jahren
Zu Sammlung hinzufügen

Du hast noch keine Sammlungen.

Herunterladen (rechte Maustaste) Teilen Fundstellen Kommentare
0:00
18:03
Im siebten Wissenshäppchen geht es um das Dependency Inversion
Principle. TL;DR Mit dem Dependency Inversion Principle, dem D aus
SOLID, richte ich Abhängigkeiten auf abstrakte Schnittstellen aus.
Die Geschäftslogik und ihre technischen Komponenten hängen dadurch
von Abstraktionen ab, wobei konkrete Implementierungen von außen
übergeben werden. So kannst du beispielsweise eine Datenbank
austauschen oder im Test durch ein Fake-Objekt ersetzen, während
die Geschäftslogik unverändert bleibt. Zusätzliche Schnittstellen
und das Zusammensetzen der Objekte erhöhen den Aufwand, weshalb ich
passende Stellen für diese Abstraktionen gezielt auswähle.
Kapitelmarken [00:00:00] Einstieg ins Dependency Inversion
Principle [00:00:50] Definition und Richtung der Abhängigkeiten
[00:01:46] Geschäftslogik und Datenbank in der Schichtenarchitektur
[00:02:30] Feste Abhängigkeiten beim Testen und Austauschen
[00:02:55] Schnittstellen und von außen übergebene Datenbanken
[00:04:55] Änderungen an technischen Komponenten entkoppeln
[00:05:46] Abhängigkeiten im Test durch Fake-Objekte ersetzen
[00:07:06] Ruby-Codebeispiel mit Payroll und Datenbank [00:08:16]
Abhängigkeiten durch Konstruktorparameter sichtbar machen
[00:09:45] Vorteile und das Open-Closed-Principle [00:10:58]
Over-Engineering und YAGNI [00:12:28] Sinnvolle Stellen für
Abstraktionen auswählen [00:13:09] Objekte zusammensetzen und
Dependency-Injection-Container [00:14:36] Praktische Hinweise zu
Schnittstellen und Testbarkeit [00:16:03] Infrastruktur im Test
austauschbar machen [00:17:08] Shownotes und Abschluss der
SOLID-Reihe Inhalt Das DIP ist das letzte der fünf
SOLID-Prinzipien. High level modules should not depend upon low
level modules. Both should depend upon abstractions. Oder:
Abstractions should not depend upon details. Details should depend
upon abstractions. Welche Abhängigkeiten werden hier "umgedreht"?
Komponenten, die andere Komponenten benötigen, erzeugen sich diese
nicht selbst, sondern bekommen sie von außen hineingegeben. Braucht
ein Service beispielsweise ein Repository um arbeiten zu können,
erzeugt er es sich nicht selbst, z.B. mit this.repo = new
Repository() in seinem Konstruktor, sondern bekommt es stattdessen
schon fertig als Parameter übergeben, z.B. public
Service(Repository repo). Das heißt, obwohl die Abhängigkeit vom
Service zum Repository geht, wird sie "umgekehrt" aufgelöst, indem
das Repository dem Service übergeben wird. Erklärung Wenn
Komponenten sich ihre Abhängigkeiten selbst erzeugen, sind diese
später nur umständlich oder gar nicht mehr austauschbar. Tests von
Komponenten, die sich ihre Abhängigkeiten selbst erzeugen, sind
schwierig/unmöglich/langsam/fehleranfällig, da die Abhängigkeiten
nicht so einfach gegen Fake-Objekte ausgetauscht werden können. Die
Abhängigkeiten sind nicht explizit in der API sichtbar. Der obige
Service "braucht" ein Repository, aber das sieht man nur, wenn man
sich den Quelltext von Service anschaut. Beim Erzeugen eines
Services ist dies nicht offensichtlich, weil er sich das Repository
"heimlich" selbst erzeugt. Beispiel Um alle Employees zu bezahlen,
erzeugt sich die Payroll ihr eigenes MySqlEmployeeRepository.
Dadurch wird auch im Test die echte Datenbank verwendet und ein
Austauschen gegen eine Oracle-Datenbank erfordert eine Anpassung
der Klasse Payroll (vgl. Open Closed Principle). class Payroll def
initialize @repo = MySqlEmployeeRepository.new end def
pay_employees @repo.find_all.each { |employee| employee.pay } end
end class MySqlEmployeeRepository def find_all puts "Accessing the
database and reading all employees" [Employee.new] * 10 end end
class Employee def pay puts "Thank you for paying me" end end
Payroll.new.pay_employees # Accessing the database and reading all
employees # Thank you for paying me # ... # Thank you for paying me
Statt dieser "harten" und versteckten Abhängigkeit sollte Payroll
sich lieber die Implementierung eines Repositorys liefern lassen.
Das erfordert nur eine kleine Umstrukturierung des Codes und
ermöglicht deutlich einfachere Tests. class Payroll def
initialize(repo) @repo = repo end def pay_employees
@repo.find_all.each { |employee| employee.pay } end end class
MySqlEmployeeRepository def find_all puts "Accessing the database
and reading all employees" [Employee.new] * 10 end end class
FakeEmployeeRepository def find_all [Employee.new] * 1 end end
class Employee def pay puts "Thank you for paying me" end end puts
"Production:"
Payroll.new(MySqlEmployeeRepository.new).pay_employees puts "Test:"
Payroll.new(FakeEmployeeRepository.new).pay_employees # Production:
# Accessing the database and reading all employees # Thank you for
paying me # ... # Thank you for paying me # Test: # Thank you for
paying me Außerdem kann Payroll nun auch ohne Anpassungen (!) mit
einer Oracle-Datenbank arbeiten. class OracleEmployeeRepository def
find_all puts "Reading employees from Oracle database"
[Employee.new] * 100 end end puts "Using Oracle:"
Payroll.new(OracleEmployeeRepository.new).pay_employees # Reading
employees from Oracle database # Thank you for paying me # ... #
Thank you for paying me Vorteile Abhängigkeiten sind explizit
sichtbar und können bei geänderten Anforderungen oder in Tests
einfach ausgetauscht werden. Neue Funktionalitäten können dem
Programm einfacher hinzugefügt werden, da durch die Abhängigkeit
von Abstraktionen oftmals auch das OCP umgesetzt wird. Nachteile
Ein Problem könnte wieder ein "Overengineering" sein. Es ist
wahrscheinlich nicht sinnvoll, jede Kleinigkeit hinter einer
Abstraktion zu verbergen. Aber wie entscheidet man, welche
Abstraktion sinnvoll ist? Bei vielen Abhängigkeiten wird das
Erstellen von Objekten (und damit auch deren Test) schwierig, weil
viel Arbeit vorab erledigt werden muss. Hier könnte dann ein
DI-Container zum Einsatz kommen. Literaturempfehlungen Wie sollte
es anders sein: Auch für das letzte SOLID-Prinzip empfehle ich noch
einmal Clean Code* von Uncle Bob. * Links Permalink zu dieser
Podcast-Episode RSS-Feed des Podcasts Dependency Inversion
Principle ArticleS.UncleBob.PrinciplesOfOod (Artikel von Uncle Bob,
Beispiel in C++) Dependency-Inversion-Prinzip – Wikipedia Das
Dependency Inversion Principle | Informatik Aktuell (Deutsches
Beispiel in C#) Zusammenfassung Abhängigkeiten auf Schnittstellen
ausrichten Beim Dependency Inversion Principle (DIP), dem letzten
der fünf SOLID-Prinzipien, hängen höher angesiedelte Module wie
meine Geschäftslogik und technische Module wie eine
Datenbankanbindung von Abstraktionen ab. Die konkreten technischen
Details richten sich ebenfalls nach diesen Abstraktionen. Damit
wird die Richtung der Abhängigkeiten umgedreht. In einer
klassischen Schichtenarchitektur hängt meine Geschäftslogik häufig
direkt von einer konkreten Datenbankklasse ab. Steht dort
ausdrücklich eine MySQL-Klasse, zieht ein Wechsel zu Oracle
Änderungen am Business-Code nach sich. Solche Änderungen können
Fehler verursachen. Auch technische Änderungen an der
Datenbankklasse können sich bis zur Geschäftslogik auswirken und
erneutes Kompilieren erforderlich machen. Eine Datenbank von außen
übergeben Für meine Geschäftslogik definiere ich eine Schnittstelle
wie IDatabase, die die benötigten Funktionen anbietet. Eine
Basisklasse kann ebenfalls als Abstraktion dienen. Die
Business-Klasse erhält von außen ein Objekt, das diese
Schnittstelle implementiert. Damit kannst du verschiedene
Datenbankklassen einsetzen: Eine MySQL-Klasse implementiert die
vereinbarte Schnittstelle und stellt die Verbindung zu MySQL her.
Für Oracle schreibst du eine weitere Klasse mit derselben
Schnittstelle, die du anschließend an die Geschäftslogik übergibst.
Die Business-Klasse kennt dabei die gemeinsame Schnittstelle.
Solange diese stabil bleibt, können Änderungen an der konkreten
Datenbankklasse getrennt bleiben; unter Umständen muss dann nur
diese Klasse neu kompiliert werden. Bei einem Interface prüft der
Compiler, ob das übergebene Objekt die Schnittstelle implementiert.
Geschäftslogik gezielt testen Wenn meine Business-Klasse selbst
eine echte Datenbankverbindung erzeugt, braucht ihr Test diese
Datenbank ebenfalls. Dadurch können Tests langsam oder
fehleranfällig werden und bei fehlender Datenbank scheitern. Mit
einer übergebenen Abhängigkeit kannst du im Test ein Fake-Objekt
einsetzen, das die passende Schnittstelle anbietet und festgelegte
Werte zurückliefert. Auch Stubs oder Mocks können Abhängigkeiten
ersetzen. So lässt sich die eigentliche Geschäftslogik gezielt
prüfen, während die beteiligten technischen Komponenten durch
Testobjekte abgedeckt werden. In meinem Ruby-Beispiel erhält eine
Payroll-Klasse, die Mitarbeiter:innen bezahlt, ihre Datenbank von
außen. Im Test übergebe ich eine Fake-Datenbank, die genau eine:n
Mitarbeiter:in liefert. Ruby hat als dynamisch typisierte Sprache
keine Interfaces; das Prinzip funktioniert dort über Objekte, die
dieselbe Schnittstelle anbieten. Abhängigkeiten sichtbar machen
Über einen Konstruktorparameter mache ich sichtbar, welche Objekte
meine Klasse braucht. Du erkennst bereits beim Erzeugen einer
Payroll, dass du eine Datenbank bereitstellen musst und in welcher
Reihenfolge die Objekte entstehen sollen. Werden benötigte Objekte
innerhalb von Methoden erzeugt, musst du diese Methoden lesen, um
die Abhängigkeiten zu entdecken. Ein Konstruktorparameter zeigt dir
den Bedarf direkt und hilft beim Schreiben von Tests sowie beim
Arbeiten am produktiven Code. Bestehenden Code erweitern Durch eine
neue Klasse, die das vorhandene Datenbankinterface implementiert,
kann meine Payroll auch mit einer weiteren Datenbank arbeiten. Die
bestehende Klasse bleibt dabei unverändert. Das unterstützt das
Open-Closed-Principle, nach dem sich Verhalten erweitern lässt,...
Episode melden

„Dependency Inversion Principle (DIP) – Wissenshäppchen #7“

Worum geht es? Danach fragen wir noch nach dem Grund.

Teilen

Mein Archiv

Deine Privatkopie der Folgen, die du nicht verlieren willst.

Podcast-Folgen verschwinden. Feeds werden auf die letzten Episoden gekürzt, Hoster räumen alte Dateien ab, Formate wechseln den Anbieter und lassen ihr Archiv zurück. Mit „Mein Archiv“ sichert podcast.de die Folgen deiner Podcasts für dich — angefangen bei den ältesten, denn die sind zuerst weg.

  • Deine gesicherten Folgen bleiben hörbar, auch wenn das Original offline geht.
  • Auch Folgen, die im heutigen Feed gar nicht mehr stehen — podcast.de kennt sie noch.
  • Herunterladen bleibt möglich, solange die Folge beim Podcaster liegt. Der zählt seine Abrufe wie bisher.
Startet bald

Sei beim Start von Mein Archiv dabei

Mein Archiv ist fast fertig. Trag dich ein, dann bekommst du eine E-Mail, sobald es losgeht – und bist von Anfang an dabei. Wir schreiben dir nur zum Start, keine Werbung, keine Weitergabe deiner Daten.

Du bekommst zuerst eine Bestätigungsmail. Abmelden geht jederzeit. Datenschutz