598 Stimmen

Warum brauche ich einen IoC-Container im Gegensatz zu einfachem DI-Code?

Ich habe mit Injektion von Abhängigkeiten (DI) für eine Weile, entweder in einem Konstruktor, einer Eigenschaft oder einer Methode. Ich hatte noch nie das Bedürfnis, eine Umkehrung der Kontrolle (IoC) Container. Je mehr ich jedoch lese, desto mehr Druck verspüre ich von der Gemeinschaft, einen IoC-Container zu verwenden.

Ich spielte mit .NET-Containern wie StructureMap , NInject , Einigkeit y Funq . Ich kann immer noch nicht sehen, wie ein IoC-Container zu profitieren / verbessern meinen Code.

Ich habe auch Angst davor, bei der Arbeit einen Container zu verwenden, weil viele meiner Kollegen Code sehen werden, den sie nicht verstehen. Viele von ihnen sind vielleicht nicht bereit, eine neue Technologie zu erlernen.

Bitte, überzeugen Sie mich, dass ich einen IoC-Container verwenden muss. Ich werde diese Argumente verwenden, wenn ich mit meinen Entwicklerkollegen bei der Arbeit spreche.

28voto

Bill Karwin Punkte 493880

Ich bin ein Fan der deklarativen Programmierung (schauen Sie sich an, wie viele SQL-Fragen ich beantworte), aber die IoC-Container, die ich mir angeschaut habe, scheinen zu geheimnisvoll für ihr eigenes Wohl.

...oder vielleicht sind die Entwickler von IoC-Containern nicht in der Lage, eine klare Dokumentation zu schreiben.

...oder aber beides ist in gewissem Maße wahr.

Ich glaube nicht, dass die Konzept eines IoC-Containers ist schlecht. Aber die Implementierung muss sowohl leistungsfähig (d. h. flexibel) genug sein, um in einer Vielzahl von Anwendungen nützlich zu sein, als auch einfach und leicht verständlich.

Wahrscheinlich sind es sechs von dem einen und ein halbes Dutzend von dem anderen. Eine reale Anwendung (kein Spielzeug oder eine Demo) ist zwangsläufig komplex und berücksichtigt viele Eckfälle und Ausnahmen von den Regeln. Entweder man kapselt diese Komplexität in imperativem Code oder in deklarativem Code. Aber man muss sie irgendwo darstellen.

27voto

Jeffrey Hantin Punkte 34609

Bei der Verwendung eines Containers geht es hauptsächlich um den Wechsel von einem imperativisch/geschrieben Stil der Initialisierung und Konfiguration zu einem deklarativ ein. Dies kann verschiedene positive Auswirkungen haben:

  • Verringern Haarballen Startroutinen für das Hauptprogramm.
  • Ermöglichung relativ tiefgreifender Rekonfigurationsmöglichkeiten während der Einsatzzeit.
  • Der Weg des geringsten Widerstands für neue Arbeiten ist die Injektion von Abhängigkeiten.

Natürlich kann es Schwierigkeiten geben:

  • Code, der ein komplexes Start-/Herunterfahr-/Lebenszyklus-Management erfordert, kann nicht ohne weiteres an einen Container angepasst werden.
  • Sie werden wahrscheinlich mit persönlichen, prozessualen und teamkulturellen Problemen zurechtkommen müssen - aber deshalb haben Sie ja gefragt...
  • Einige der Toolkits werden schnell selbst zu Schwergewichten und fördern die Art von tiefer Abhängigkeit, gegen die sich viele DI-Container anfangs gewehrt haben.

23voto

Sam Saffron Punkte 124121

Das klingt für mich wie Sie haben bereits Ihren eigenen IoC-Container gebaut (unter Verwendung der verschiedenen Muster, die von Martin Fowler beschrieben wurden) und fragen, warum die Implementierung eines anderen besser ist als Ihre.

Sie haben also einen Haufen Code, der bereits funktioniert. Und Sie fragen sich, warum Sie ihn durch die Implementierung eines anderen ersetzen wollen.

Profis für die Berücksichtigung eines IoC-Containers eines Drittanbieters

  • Sie erhalten kostenlose Fehlerbehebungen
  • Der Entwurf der Bibliothek kann besser sein als Ihrer
  • Die Menschen sind möglicherweise bereits mit der jeweiligen Bibliothek vertraut
  • Die Bibliothek kann schneller sein als Ihre
  • Vielleicht gibt es Funktionen, die Sie gerne implementiert hätten, aber nie die Zeit dazu hatten (haben Sie einen Service Locater?)

Nachteile

  • Sie bekommen die Bugs kostenlos eingeführt :)
  • Das Design der Bibliothek kann schlechter sein als Ihres
  • Sie müssen eine neue API lernen
  • Zu viele Funktionen, die Sie nie nutzen werden
  • Es ist normalerweise schwieriger, Code zu debuggen, den Sie nicht geschrieben haben.
  • Die Migration von einem früheren IoC-Container kann mühsam sein

Wägen Sie also Ihre Vor- und Nachteile ab und treffen Sie eine Entscheidung.

17voto

Steven Lyons Punkte 7958

Ich denke, der größte Teil des Wertes einer IoC wird durch die Verwendung von DI erzielt. Da Sie das bereits tun, ist der Rest des Nutzens inkrementell.

Der Wert, den Sie erhalten, hängt von der Art der Anwendung ab, an der Sie arbeiten:

  • Bei mandantenfähigen Systemen kann der IoC-Container einen Teil des Infrastrukturcodes für das Laden verschiedener Client-Ressourcen übernehmen. Wenn Sie eine kundenspezifische Komponente benötigen, verwenden Sie einen benutzerdefinierten Selektor, um die Logik zu handhaben, und kümmern Sie sich nicht um Ihren Client-Code. Sie können dies sicherlich selbst erstellen, aber Hier ein Beispiel wie ein IoC helfen kann.

  • Mit vielen Punkten der Erweiterbarkeit kann IoC verwendet werden, um Komponenten aus der Konfiguration zu laden. Dies ist eine übliche Sache zu bauen, aber Werkzeuge werden durch den Container zur Verfügung gestellt.

  • Wenn Sie AOP für einige bereichsübergreifende Belange verwenden möchten, können Sie die IoC bietet Haken um Methodenaufrufe abzufangen. Dies wird in Projekten seltener ad-hoc gemacht, aber IoC macht es einfacher.

Ich habe schon früher solche Funktionen geschrieben, aber wenn ich jetzt eine dieser Funktionen benötige, würde ich lieber ein vorgefertigtes und getestetes Tool verwenden, wenn es zu meiner Architektur passt.

Wie bereits von anderen erwähnt, können Sie auch zentral konfigurieren, welche Klassen Sie verwenden möchten. Dies kann zwar eine gute Sache sein, hat aber den Preis von Irreführung und Komplikation. Die Kernkomponenten für die meisten Anwendungen werden nicht so oft ersetzt, so dass der Kompromiss etwas schwieriger zu treffen ist.

Ich verwende einen IoC-Container und schätze die Funktionalität, muss aber zugeben, dass ich den Kompromiss bemerkt habe: Mein Code wird auf Klassenebene klarer und auf Anwendungsebene weniger klar (d.h. Visualisierung des Kontrollflusses).

16voto

Maxm007 Punkte 1170

Ich bin ein genesender IOC-Süchtiger. Ich finde es schwer, die Verwendung von IOC für DI in den meisten Fällen heutzutage zu rechtfertigen. IOC-Container opfern die Kompilierzeitüberprüfung und bieten im Gegenzug angeblich ein "einfaches" Setup, eine komplexe Lebenszeitverwaltung und eine fliegende Entdeckung von Abhängigkeiten zur Laufzeit. Ich finde, dass der Verlust der Kompilierzeitüberprüfung und die daraus resultierende Laufzeitmagie bzw. die Ausnahmen in den allermeisten Fällen den Schnickschnack nicht wert sind. In großen Unternehmensanwendungen kann es dadurch sehr schwierig werden, die Vorgänge zu verfolgen.

Ich kaufe das Zentralisierungsargument nicht, weil Sie statische Einrichtung sehr leicht zentralisieren können, indem Sie eine abstrakte Fabrik für Ihre Anwendung verwenden und die Objekterstellung religiös auf die abstrakte Fabrik aufschieben, d. h. ordnungsgemäßes DI durchführen.

Warum nicht eine statische, magiefreie DI wie diese:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}

interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }

    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

Ihre Container-Konfiguration ist Ihre abstrakte Fabrik-Implementierung, Ihre Registrierungen sind Implementierungen von abstrakten Mitgliedern. Wenn Sie eine neue Singleton-Abhängigkeit benötigen, fügen Sie einfach eine weitere abstrakte Eigenschaft zur abstrakten Fabrik hinzu. Wenn Sie eine transiente Abhängigkeit benötigen, fügen Sie einfach eine weitere Methode hinzu und injizieren sie als Func<>.

Vorteile:

  • Die gesamte Konfiguration der Einrichtung und Objekterstellung ist zentralisiert.
  • Konfiguration ist nur Code
  • Die Kompilierzeitprüfung macht die Wartung einfach, da Sie nicht vergessen können, Ihre Registrierungen zu aktualisieren.
  • Keine Reflexionsmagie während der Laufzeit

Skeptikern empfehle ich, es beim nächsten Projekt auf der grünen Wiese zu versuchen und sich ehrlich zu fragen, an welcher Stelle man den Container braucht. Es ist einfach, einen IOC-Container zu einem späteren Zeitpunkt einzubauen, da Sie lediglich eine Fabrikimplementierung durch ein IOC-Container-Konfigurationsmodul ersetzen.

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