IT-Berufe-Podcast

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


Listen Later

Im siebten Wissenshäppchen geht es um das Dependency Inversion Principle.

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#)
        • ...more
          View all episodesView all episodes
          Download on the App Store

          IT-Berufe-PodcastBy Stefan Macke

          • 5
          • 5
          • 5
          • 5
          • 5

          5

          1 ratings


          More shows like IT-Berufe-Podcast

          View all
          Streitkräfte und Strategien by NDR Info

          Streitkräfte und Strategien

          46 Listeners

          Sternengeschichten by Florian Freistetter

          Sternengeschichten

          43 Listeners

          Geschichten aus der Geschichte by Richard Hemmer und Daniel Meßner

          Geschichten aus der Geschichte

          189 Listeners

          c’t uplink - der IT-Podcast aus Nerdistan by c’t Magazin

          c’t uplink - der IT-Podcast aus Nerdistan

          10 Listeners

          MORD AUF EX by Leonie Bartsch & Linn Schütze

          MORD AUF EX

          130 Listeners

          {ungeskriptet} - Gespräche, die dich weiter bringen by Ben Berndt

          {ungeskriptet} - Gespräche, die dich weiter bringen

          32 Listeners