Verkaufen Sie mich bitte auf IoC-Containern

17

Ich habe mehrere empfohlene Verwendung von IoC-Containern im Code gesehen. Die Motivation ist einfach. Nehmen Sie den folgenden in Abhängigkeit injizierten Code:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest(
        std::auto_ptr<Dependency> d = std::auto_ptr<Dependency>(new ConcreteDependency)
    ) : d_(d)
    {
    }
};


TEST(UnitUnderTest, Example)
{
    std::auto_ptr<Dependency> dep(new MockDependency);
    UnitUnderTest uut(dep);
    //Test here
}

In:

class UnitUnderTest
{
    std::auto_ptr<Dependency> d_;
public:
    UnitUnderTest()
    {
        d_.reset(static_cast<Dependency *>(IocContainer::Get("Dependency")));
    }
};


TEST(UnitUnderTest, Example)
{
    UnitUnderTest uut;
    //Test here
}

//Config for IOC container normally
<Dependency>ConcreteDependency</Dependency>

//Config for IOC container for testing
<Dependency>MockDependency</Dependency>

(Das obige ist natürlich ein hypothetisches C ++ - Beispiel)

Ich stimme zu, dass dies die Schnittstelle der Klasse vereinfacht, indem der Parameter für den Abhängigkeitskonstruktor entfernt wird. Ich denke jedoch, dass die Heilung aus mehreren Gründen schlechter ist als die Krankheit. Erstens, und dies ist für mich sehr wichtig, macht dies Ihr Programm von einer externen Konfigurationsdatei abhängig. Wenn Sie eine einzelne Binärbereitstellung benötigen, können Sie diese Art von Containern einfach nicht verwenden. Das zweite Problem ist, dass die API jetzt schwach und schlechter ist und streng getippt ist. Der Beweis (in diesem hypothetischen Beispiel) ist das Zeichenfolgenargument für den IoC-Container und die Besetzung des Ergebnisses.

Gibt es also noch andere Vorteile bei der Verwendung dieser Art von Behältern oder stimme ich denjenigen, die die Behälter empfehlen, einfach nicht zu?

Billy ONeal
quelle
1
Der Beispielcode sieht aus wie ein wirklich schlechtes Beispiel für eine IoC-Implementierung. Ich bin nur mit C # vertraut, aber gibt es sicherlich eine Möglichkeit, Konstruktorinjektion und programmatische Konfiguration auch in C ++ zu haben?
rmac

Antworten:

12

Bei einer großen Anwendung mit vielen Schichten und vielen beweglichen Teilen sind die Nachteile im Vergleich zu den Vorteilen eher gering.

Der Container "vereinfacht die Schnittstelle" der Klasse, dies ist jedoch sehr wichtig. Der Container ist eine Lösung für das Problem, das durch die Abhängigkeitsinjektion entsteht. Dabei müssen Abhängigkeiten überall durch Objektdiagramme und über Funktionsbereiche hinweg übertragen werden. Sie haben hier ein kleines Beispiel mit einer Abhängigkeit: Was wäre, wenn dieses Objekt drei Abhängigkeiten hätte und die Objekte, die davon abhängen, mehrere Objekte, die von ihnen abhängen , und so weiter? Ohne Container sind die Objekte an der Spitze dieser Abhängigkeitsketten letztendlich dafür verantwortlich, alle Abhängigkeiten in der gesamten Anwendung zu verfolgen.

Es gibt auch verschiedene Arten von Behältern. Nicht alle von ihnen sind strikt typisiert, und nicht alle erfordern Konfigurationsdateien.

Nlawalker
quelle
3
Aber ein großes Projekt verschlimmert die Probleme, die ich bereits erwähnt habe . Wenn das Projekt groß ist, wird die Konfigurationsdatei selbst zu einem großen Abhängigkeitsmagneten, und es wird noch einfacher, wenn ein Tippfehler zur Laufzeit zu undefiniertem Verhalten führt. (dh ein Tippfehler in der Konfigurationsdatei würde dazu führen, dass die Besetzung undefiniertes Verhalten verursacht, wenn der falsche Typ zurückgegeben wird). Abhängigkeiten von Abhängigkeiten spielen hier keine Rolle. Entweder kümmert sich der Konstruktor darum, sie zu erstellen, oder der Test wird die Abhängigkeit der obersten Ebene verhöhnen.
Billy ONeal
... Fortsetzung ... Wenn das Programm nicht getestet wird, wird die konkrete Standardabhängigkeit bei jedem Schritt instanziiert. Haben Sie Beispiele für andere Arten von Behältern?
Billy ONeal
4
TBH Ich habe nicht viel Erfahrung mit DI-Implementierungen, aber werfen Sie einen Blick auf MEF in .NET, das zwar eingegeben werden kann, aber nicht muss, keine Konfigurationsdatei hat und konkrete Implementierungen von Schnittstellen "registriert" sind. auf den Klassen selbst. Übrigens, wenn Sie sagen: "Der Konstruktor kümmert sich um die Herstellung" - wenn Sie das tun (auch als "Injektion des Konstruktors des armen Mannes" bezeichnet), dann brauchen Sie keinen Container. Der Vorteil eines Containers gegenüber diesem besteht darin, dass der Container Informationen zu all diesen Abhängigkeiten zentralisiert.
Nlawalker
+1 für diesen letzten Kommentar - der letzte Satz gibt es die Antwort :)
Billy ONeal
1
Tolle Antwort, die mir geholfen hat, alles klar zu machen. Es geht nicht nur darum, das Boilerplate zu vereinfachen, sondern auch darum, dass die Elemente oben in der Abhängigkeitskette genauso naiv sind wie die Elemente unten.
Christopher Berman
4

Der Guice IoC - Framework von Java beruht nicht auf einer Konfigurationsdatei , sondern auf Konfigurationscode . Dies bedeutet, dass sich der Code der Konfiguration nicht von dem Code unterscheidet, aus dem sich Ihre eigentliche Anwendung zusammensetzt, und dass er überarbeitet werden kann usw.

Ich glaube, dass Guice das Java-IoC-Framework ist, das endlich die richtige Konfiguration hat.


quelle
Gibt es ähnliche Beispiele für C ++?
Billy ONeal
@ Billy, ich kenne mich mit C ++ nicht aus, um sagen zu können.
0

Diese großartige SO-Antwort von Ben Scheirman enthält einige Codebeispiele in C #. Einige Vorteile von IoC-Containern (DI):

  • Ihre Abhängigkeitskette kann verschachtelt werden und es wird schnell unhandlich, sie manuell zu verkabeln.
  • Ermöglicht die Verwendung der aspektorientierten Programmierung .
  • Deklarative und verschachtelte Datenbanktransaktionen.
  • Deklarative & verschachtelte Arbeitseinheit.
  • Protokollierung.
  • Vor- / Nachbedingungen (Design by Contract).
dodgy_coder
quelle
1
Ich verstehe nicht, wie all diese Dinge mit einer einfachen Konstruktorinjektion nicht implementiert werden können.
Billy ONeal
Mit einem guten Design kann die Konstruktorinjektion oder eine andere Methode für Sie ausreichen. Die Vorteile dieser IoC-Container sind möglicherweise nur in bestimmten Fällen gegeben. Das mit INotifyPropertyChanged in einem WPF-Projekt gegebene Beispiel war ein solches Beispiel.
dodgy_coder