C++ DIP (Dependency Inversion Principle) ?



  • Hallo!

    Ich weiß soweit was ein Interface ist, wozu man ein Interface braucht. Aber das Dependency Inversion Principle , im bezug auf Interfaces, habe ich irgendwie nicht ganz verstanden.

    Zu frage: "Was genau sagt das DPI aus im bezug auf Interfaces?"

    Schonmal vielen dank im voraus !



  • Du meinst Dependency Injection bzw. Inversion of Control?!
    Und dabei werden üblicherweise Interfaces (d.h. in C++ abstrakte Klassen) als Parameter verwendet, um zum einen die Erzeugung außerhalb der Klasse bzw. Funktion zu halten als auch die Möglichkeit zu bieten, verschiedene Implementierungen zu benutzen (bes. sinnvoll im Zusammenhang mit Unit-Tests die Verwendung von Mock-Objekten).



  • Dependency Injection wird aber mit DI abgekürzt...



  • @NoIDE sagte in C++ DIP (Dependency Inversion Principle) ?:

    Dependency Injection wird aber mit DI abgekürzt...

    Und wie hilft deine Antwort uns weiter?

    Ich hab mal eine ganz kurze suche gemacht.
    DI ist, soweit ich das verstanden habe, eine Möglichkeit DIP umzusetzen.
    (Wenn man Wikipedia in der Hinsicht vertrauen kann.)

    Denn "Dependency Inversion Principle", wie der Name schon sagt, beschreibt nur ein Prinzip und keine konkrete Umsetzung des Prinzips.

    https://en.wikipedia.org/wiki/Dependency_inversion_principle#Related_patterns

    Various patterns such as Plugin, Service Locator,[3] or Dependency injection[4][5] are employed to facilitate the run-time provisioning of the chosen low-level component implementation to the high-level component.

    https://en.wikipedia.org/wiki/Dependency_Injection

    Dependency injection is often used to keep code in-line with the dependency inversion principle.



  • Dieser Beitrag wurde gelöscht!


  • Oh, dann habe ich mich auch ein bißchen vertan - ich hatte nicht an das 5. Prinzip von SOLID gedacht: Dependency Inversion Prinzip.



  • @Sebi1412
    Vielleicht hilft ein Beispiel.

    Die "naive" Variante ohne DIP:

    class FooTransport final {
    public:
        explicit FooTransport(std::string_view serverAddress);
        void send(ComposedMessage const& message);
        // ...
    };
    
    class Messenger final {
    public:
        Messenger() : m_transport(readConfigFile().getEntry("ServerAddress")) {
        }
    
        // ...
    
    private:
        void doSend(ComposedMessage const& message) {
            m_transport.send(message);
        }
    
        FooTransport m_transport;
    };
    
    int main() {
        Messenger m;
        m.run();
    }
    

    Hier ist Messenger direkt von der konkreten Transport-Klasse FooTransport abhängig.

    Mit DIP:

    class AbstractTransport {
    public:
        virtual ~AbstractTransport() = default;
        virtual void send(ComposedMessage const& message) = 0;
        // ...
    };
    
    class FooTransport final : public AbstractTransport {
    public:
        explicit FooTransport(std::string_view serverAddress);
        void send(ComposedMessage const& message) override;
        // ...
    };
    
    class Messenger final {
    public:
        Messenger(std::unique_ptr<AbstractTransport> transport) : m_transport(std::move(transport)) {
        }
    
        // ...
    
    private:
        void doSend(ComposedMessage const& message) {
            m_transport->send(message);
        }
    
        std::unique_ptr<AbstractTransport> const m_transport;
    };
    
    int main() {
        auto const serverAddress = readConfigFile().getEntry("ServerAddress");
        Messenger m{std::make_unique<FooTransport>(serverAddress)};
        m.run();
    }
    

    Hier ist Messenger nicht mehr von FooTransport abhängig. Dafür sind beide von der abstrakten Interface-Klasse AbstractTransport abhängig. FooTransport weil es das von AbstractTransport vorgeschriebene Verhalten umsetzen muss, und Messenger weil es ein AbstractTransport Objekt benötigt und sich auf das von AbstractTransport vorgeschriebene Verhalten verlässt.
    Und das wird als "Dependency Inversion" bezeichnet.

    Den Umstand dass das "Transport" Objekt von aussen an das Messenger Objekt übergeben wird nennt man "Dependency Injection". Und wenn man es so wie hier an den Konstruktor übergibt, dann nennt man es auch "Constructor Injection".



  • @Th69 sagte in C++ DIP (Dependency Inversion Principle) ?:

    Oh, dann habe ich mich auch ein bißchen vertan - ich hatte nicht an das 5. Prinzip von SOLID gedacht: Dependency Inversion Prinzip.

    Ach, auf einmal ist mein Einwand berechtigt gewesen? Nach @firefly war dieser doch total nutzlos?



  • Habe es leider erst jetzt gelesen, aber nochmal vielen herzlichen dank an alle Kommentare die haben mir aufjedenfall weitergeholfen mit dem Thema SOLID bzw. DIP wärmer zu werden und es zu verstehen was DIP ist und wieso DIP sinnvoll ist vor allem in der OOP.



  • @Sebi1412
    p.s.: Dependency Injection muss natürlich nicht mit einer abstrakten Basisklasse kombiniert werden. Man könnte in dem Beispiel von oben auch direkt eine FooTransport übergeben. Das kann auch manchmal Sinn machen - wobei mir jetzt aus dem Stegreif kein Beispiel einfällt.

    Am flexibelsten ist es allerding mit der abstrakten Basisklasse. Auch wenn man es im eigentlichen Programm nicht braucht, ist es in Tests oft sehr praktisch. z.B. kann man so die Messenger Klasse testen, ohne dass man einen echten Server braucht mit dem man die Messenger Objekte kommunizieren lassen kann. Statt dessen macht man für den Test eine spezielle TestTransport Klasse, die ebenfalls von AbstractTransport abgeleitet ist, und bloss so tut als ob sie mit einem Server kommunizieren würde.