362 Stimmen

Wie kann man den Konstruktor-Wahnsinn der Dependency Injection vermeiden?

Ich stelle fest, dass meine Konstrukteure allmählich wie folgt aussehen:

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

mit ständig wachsender Parameterliste. Da "Container" ist meine Abhängigkeit Injektion Container, warum kann ich nicht einfach dies tun:

public MyClass(Container con)

für jede Klasse? Was sind die Nachteile? Wenn ich das tue, fühlt es sich an, als würde ich ein verherrlichtes statisches Element verwenden. Bitte teilen Sie Ihre Gedanken über IoC und Dependency Injection Wahnsinn.

4voto

Craig Brunetti Punkte 489

Ich habe diesen ganzen Thread zweimal gelesen, und ich glaube, dass die Leute mit dem antworten, was sie wissen, und nicht mit dem, was gefragt wird.

Die ursprüngliche Frage von JP sieht so aus, dass er Objekte konstruiert, indem er einen Resolver und dann eine Reihe von Klassen sendet, aber wir gehen davon aus, dass diese Klassen/Objekte selbst Dienste sind, die reif für Injektionen sind. Was, wenn sie es nicht sind?

JP, wenn Sie die DI nutzen wollen et die Herrlichkeit der Vermischung von Injektion mit kontextuellen Daten, keines dieser Muster (oder vermeintlichen "Anti-Patterns") befasst sich speziell mit diesem Thema. Eigentlich läuft es darauf hinaus, ein Paket zu verwenden, das Sie bei einem solchen Unterfangen unterstützt.

Container.GetSevice<MyClass>(someObject1, someObject2)

... dieses Format wird nur selten unterstützt. Ich glaube, die Schwierigkeit, eine solche Unterstützung zu programmieren, zusammen mit der miserablen Leistung, die mit der Implementierung verbunden wäre, macht es für Open-Source-Entwickler unattraktiv.

Aber es sollte getan werden, weil ich in der Lage sein sollte, eine Fabrik für MyClass'es zu erstellen und zu registrieren, und diese Fabrik sollte in der Lage sein, Daten/Eingaben zu empfangen, die nicht in einen "Dienst" nur um der Übergabe von Daten willen gedrückt werden. Wenn es bei "Anti-Pattern" um negative Konsequenzen geht, dann ist das Erzwingen von künstlichen Diensttypen für die Übergabe von Daten/Modellen sicherlich negativ (ähnlich wie Ihr Gefühl, wenn Sie Ihre Klassen in einem Container verpacken. Es gilt derselbe Instinkt).

Es gibt jedoch Rahmenbedingungen, die helfen können, auch wenn sie ein wenig hässlich aussehen. Zum Beispiel, Ninject:

Erstellen einer Instanz mit Ninject mit zusätzlichen Parametern im Konstruktor

Das ist für .NET, ist beliebt, und ist noch lange nicht so sauber, wie es sein sollte, aber ich bin sicher, es gibt etwas in jeder Sprache, die Sie wählen, zu verwenden.

3voto

Ronijo Punkte 51

Dies ist der Ansatz, den ich verwende

public class Hero
{

    [Inject]
    private IInventory Inventory { get; set; }

    [Inject]
    private IArmour Armour { get; set; }

    [Inject]
    protected IWeapon Weapon { get; set; }

    [Inject]
    private IAction Jump { get; set; }

    [Inject]
    private IInstanceProvider InstanceProvider { get; set; }

}

Hier ist ein grober Ansatz, wie man die Injektionen durchführt und den Konstruktor nach der Injektion von Werten ausführt. Dies ist ein voll funktionsfähiges Programm.

public class InjectAttribute : Attribute
{

}

public class TestClass
{
    [Inject]
    private SomeDependency sd { get; set; }

    public TestClass()
    {
        Console.WriteLine("ctor");
        Console.WriteLine(sd);
    }
}

public class SomeDependency
{

}

class Program
{
    static void Main(string[] args)
    {
        object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));

        // Get all properties with inject tag
        List<PropertyInfo> pi = typeof(TestClass)
            .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
            .Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();

        // We now happen to know there's only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
        pi[0].SetValue(tc, new SomeDependency(), null);

        // Find the right constructor and Invoke it. 
        ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
        ci.Invoke(tc, null);

    }
}

Ich arbeite derzeit an einem Hobbyprojekt, das wie folgt funktioniert https://github.com/Jokine/ToolProject/tree/Core

3voto

Marius Punkte 85

Das Einspritzen des Containers ist eine Abkürzung, die Sie letztendlich bereuen werden.

Die Überinjektion ist nicht das Problem, sondern in der Regel ein Symptom für andere strukturelle Mängel, insbesondere für die Trennung der Konzerne. Dies ist nicht nur ein Problem, sondern kann viele Ursachen haben, und was die Behebung so schwierig macht, ist die Tatsache, dass man sich mit all diesen Problemen befassen muss, manchmal sogar gleichzeitig (denken Sie an das Entwirren von Spaghetti).

Hier ist eine unvollständige Liste der Dinge, auf die Sie achten sollten

Schlechtes Domain-Design (Aggregate Root's . etc)

Unzureichende Trennung von Belangen (Dienstkomposition, Befehle, Abfragen) Siehe CQRS und Event Sourcing.

OR Mappers (Vorsicht, diese Dinge können zu Problemen führen)

Ansichtsmodelle und andere DTOs (niemals wiederverwenden, und versuchen, sie auf ein Minimum zu beschränken !!!!)

-7voto

David W Crook Punkte 43

Welches Dependency Injection Framework verwenden Sie? Haben Sie versucht, mit Setter basierte Injektion stattdessen?

Der Vorteil der konstruktorbasierten Injektion ist, dass sie für Java-Programmierer, die keine DI-Frameworks verwenden, natürlich aussieht. Sie brauchen 5 Dinge, um eine Klasse zu initialisieren, dann haben Sie 5 Argumente für Ihren Konstruktor. Der Nachteil ist, was Sie bemerkt haben, es wird unhandlich, wenn Sie viele Abhängigkeiten haben.

Mit Spring können Sie die erforderlichen Werte stattdessen mit Settern übergeben und @required-Annotationen verwenden, um zu erzwingen, dass sie injiziert werden. Der Nachteil ist, dass Sie den Initialisierungscode aus dem Konstruktor in eine andere Methode verschieben müssen und Spring diese Methode aufrufen muss, nachdem alle Abhängigkeiten injiziert wurden, indem Sie sie mit @PostConstruct markieren. Ich bin mir bei anderen Frameworks nicht sicher, aber ich nehme an, dass sie etwas Ähnliches tun.

Beide Wege funktionieren, es ist eine Frage der Vorliebe.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X