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

598

Ich habe Dependency Injection (DI) für eine Weile verwendet und entweder in einen Konstruktor, eine Eigenschaft oder eine Methode injiziert. Ich hatte nie das Bedürfnis, einen IoC-Container ( Inversion of Control ) zu verwenden. Je mehr ich jedoch lese, desto mehr Druck verspüre ich von der Community, einen IoC-Container zu verwenden.

Ich habe mit .NET-Containern wie StructureMap , NInject , Unity und Funq gespielt . Ich sehe immer noch nicht, wie ein IoC-Container meinem Code zugute kommt / ihn verbessert.

Ich habe auch Angst, einen Container bei der Arbeit zu verwenden, weil viele meiner Mitarbeiter Code sehen, den sie nicht verstehen. Viele von ihnen zögern möglicherweise, neue Technologien zu erlernen.

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

Vadim
quelle
6
Gute Frage. Ich beantworte dies in Kommentaren, da mir die Idee des IOC sehr neu ist. Es sieht aus wie die Idee von Plug-and-Play-Komponenten und loser Kupplung. Ob Sie anstelle der aktuellen Komponente eine andere Komponente verwenden müssen, hängt vom Bedarf ab. Die Verwendung von IOC dient dazu, den Code für eine solche Änderung vorzubereiten, falls IMO auftritt.
Shahkalpesh
3
@shahkalpesh aber ich kann lose Kopplung mit einfachem DI erreichen. Ich verstehe jedoch Ihren Standpunkt, falls ich eine Konfigurationsdatei verwende. Ich bin mir jedoch nicht sicher, ob ich eine Konfigurationsdatei verwenden möchte. Konfigurationsdateien sind sehr ausführlich, haben Schwierigkeiten beim Refactoring und beim Wechseln zwischen mehreren Dateien.
Vadim
110
Fügen Sie einfach einen Kommentar hinzu, da Sie anscheinend nach Gründen suchen, um IoC hauptsächlich zu verwenden. Aber zu Ihrem Problem muss noch etwas anderes gesagt werden. Sie können Ihren Code nicht auf die Ebene der schlechteren Person in Ihrem Team schreiben oder aus Angst, dass sie nicht lernen möchte. Sie haben die Verantwortung, nicht nur professionell zu sein, sondern auch für Ihre Zukunft zu wachsen, und Ihr Team sollte Sie nicht zurückhalten.
Kevin Sheffield
4
@ Kevin, eigentlich verwenden wir IoC. Es war nicht so schwer wie gedacht, alle über IoC zu unterrichten.
Vadim
21
Ich habe keine Ahnung, warum Moderatoren massiv hochgelobte und nützliche Fragen schließen. Vielleicht versteht Will in diesem Fall die Frage nicht oder versteht nicht, wofür die Leute Stackexchange verwenden? Wenn es "nicht gut zum Q & A-Format von stackexchange passt", denken Sie vielleicht, dass Ihre Richtlinie falsch ist und nicht alle diese Hunderte nützlicher Fragen mit Hunderten von Up-Votes und Up-Voted-nützlichen Antworten.
NickG

Antworten:

441

Wow, ich kann nicht glauben, dass Joel dies befürworten würde:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

darüber:

var svc = IoC.Resolve<IShippingService>();

Viele Leute wissen nicht, dass Ihre Abhängigkeitskette verschachtelt werden kann, und es wird schnell unhandlich, sie manuell zu verkabeln. Selbst in Fabriken lohnt sich die Vervielfältigung Ihres Codes nicht.

IoC-Container können ja komplex sein. Aber für diesen einfachen Fall habe ich gezeigt, dass es unglaublich einfach ist.


Okay, lassen Sie uns das noch mehr rechtfertigen. Angenommen, Sie haben einige Entitäten oder Modellobjekte, die Sie an eine intelligente Benutzeroberfläche binden möchten. Diese intelligente Benutzeroberfläche (wir nennen sie Shindows Morms) möchte, dass Sie INotifyPropertyChanged implementieren, damit sie die Nachverfolgung von Änderungen vornehmen und die Benutzeroberfläche entsprechend aktualisieren kann.

"OK, das klingt nicht so schwer", also fängst du an zu schreiben.

Sie beginnen damit:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..und am Ende mit diesem :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Das ist ekelhafter Installationscode, und ich behaupte, wenn Sie solchen Code von Hand schreiben, stehlen Sie von Ihrem Kunden . Es gibt bessere und intelligentere Arbeitsweisen.

Hören Sie jemals diesen Begriff, arbeiten Sie klüger, nicht härter?

Stellen Sie sich vor, ein kluger Kerl in Ihrem Team kam und sagte: "Hier ist ein einfacher Weg."

Wenn Sie Ihre Eigenschaften virtuelle machen ( zu beruhigen, ist es nicht so große Sache) , dann können wir weben automatisch in dieser Eigenschaft Verhalten. (Dies nennt man AOP, aber mach dir keine Sorgen um den Namen, konzentriere dich darauf, was es für dich tun wird)

Je nachdem, welches IoC-Tool Sie verwenden, können Sie Folgendes tun:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Poof! Das gesamte manuelle INotifyPropertyChanged BS wird jetzt automatisch für Sie auf jedem virtuellen Eigenschaftssetzer des betreffenden Objekts generiert.

Ist das Magie? JA ! Wenn Sie der Tatsache vertrauen können, dass dieser Code seine Aufgabe erfüllt, können Sie die gesamte Eigenschaft, die mumbo-jumbo umschließt, sicher überspringen. Sie müssen geschäftliche Probleme lösen.

Einige andere interessante Anwendungen eines IoC-Tools für AOP:

  • Deklarative und verschachtelte Datenbanktransaktionen
  • Deklarative und verschachtelte Arbeitseinheit
  • Protokollierung
  • Pre / Post-Bedingungen (Design by Contract)
Ben Scheirman
quelle
28
Hoppla, Sie haben vergessen, alle Eigenschaften virtuell zu machen, und es hat nicht funktioniert. Ist die Zeit, die Sie für das Debuggen dieses Diebstahls benötigen, auch für Ihren Client erforderlich? (Entschuldigung, wenn Sie DynamicProxy nicht verwenden .: P)
Thom
17
Mit dem INotifyPropertyChanged-Wrapper opfern Sie viel Klarheit. Wenn Sie mehr als ein paar Minuten brauchen, um diese Installation zu schreiben, dann sind Sie im falschen Geschäft. Weniger ist nicht mehr, wenn es um die Ausführlichkeit Ihres Codes geht!
überstanden
39
@overstood - Ich bin völlig anderer Meinung. Erstens, wo spielt Klarheit in diesem Beispiel eine Rolle? Dem Verbraucher am nächsten. Der Verbraucher fragt ausdrücklich nach INotifyPropertyChanged. Nur weil wir können diese Art von Code mit Mikro- oder Makrocodegenerierung erstellen, bedeutet nicht , es ist eine gute Idee , es zu schreiben. Diese INotifyPropertyChanged-Gunkel ist nur rot, banal, sanitär und beeinträchtigt tatsächlich die Lesbarkeit der betreffenden Klasse.
Ben Scheirman
31
Markiert, weil Sie das Abhängigkeitsinjektionsmuster und das Service Locator-Muster verwechseln. Ersteres soll anstelle von Letzterem verwendet werden. Beispielsweise sollte ein Objekt (oder das gesamte System) niemals Resolve (...) aufrufen, um eine Instanz von IShippingService abzurufen. Die Objekte, die einen IShippingService benötigen, sollten einen Verweis auf die Instanz erhalten, die sie beim Erstellen verwenden sollen, und eine höhere Ebene ist für die Verbindung der beiden Objekte verantwortlich.
Nat
30
Was auch immer dies ist (IoC, DynamicProxy, AOP oder schwarze Magie ), kann mich jemand mit einem konkreten Artikel / einer spezifischen Dokumentation im Framework verknüpfen, die weitere Details dazu enthält?
Fostandy
138

Ich bin bei dir, Vadim. IoC-Container haben ein einfaches, elegantes und nützliches Konzept und müssen mit einem 200-seitigen Handbuch zwei Tage lang studiert werden.

Ich persönlich bin ratlos darüber, wie die IoC-Community einen schönen, eleganten Artikel von Martin Fowler in eine Reihe komplexer Frameworks umgewandelt hat, die normalerweise Handbücher mit 200 bis 300 Seiten enthalten.

Ich versuche nicht zu urteilen (HAHA!), Aber ich denke, dass Leute, die IoC-Container verwenden, (A) sehr klug sind und (B) kein Einfühlungsvermögen für Leute haben, die nicht so klug sind wie sie. Alles macht für sie vollkommen Sinn, so dass sie Schwierigkeiten haben zu verstehen, dass viele gewöhnliche Programmierer die Konzepte verwirrend finden. Es ist der Fluch des Wissens . Die Leute, die IoC-Container verstehen, haben Probleme zu glauben, dass es Leute gibt, die das nicht verstehen.

Der wertvollste Vorteil der Verwendung eines IoC-Containers besteht darin, dass Sie einen Konfigurationsschalter an einem Ort haben können, mit dem Sie beispielsweise zwischen Testmodus und Produktionsmodus wechseln können. Angenommen, Sie haben zwei Versionen Ihrer Datenbankzugriffsklassen ... eine Version, die aggressiv protokolliert und viele Überprüfungen durchgeführt hat, die Sie während der Entwicklung verwendet haben, und eine andere Version ohne Protokollierung oder Überprüfung, die für die Produktion schreiend schnell war. Es ist schön, an einem Ort zwischen ihnen wechseln zu können. Auf der anderen Seite ist dies ein ziemlich triviales Problem, das ohne die Komplexität von IoC-Containern auf einfachere Weise einfach zu handhaben ist.

Ich glaube, wenn Sie IoC-Container verwenden, wird Ihr Code ehrlich gesagt viel schwieriger zu lesen. Die Anzahl der Stellen, an denen Sie suchen müssen, um herauszufinden, was der Code versucht, steigt um mindestens eine. Und irgendwo im Himmel schreit ein Engel.

Joel Spolsky
quelle
81
Ich denke, Sie haben Ihre Fakten ein bisschen durcheinander gebracht Joel. IoC und Container kamen zuerst, dann die Abhandlung von Martin, nicht umgekehrt.
Glenn Block
55
Mit den meisten Containern können Sie sehr schnell loslegen. Mit Autofac, Strukturkarte, Einheit, Objekt usw. sollte der Container in etwa 5 Minuten funktionieren. Ja, sie haben erweiterte Funktionen, aber Sie brauchen diese nicht, um auf den Weg zu kommen.
Glenn Block
58
Joel Ich habe immer genauso gedacht wie du. Dann habe ich buchstäblich und ernsthaft fünf Minuten damit verbracht, herauszufinden, wie man das grundlegendste Setup zum Laufen bringt, und war erstaunt über die 80/20-Regel in Aktion. Dies macht den Code besonders in einfachen Fällen viel sauberer.
Justin Bozonier
55
Ich programmiere seit weniger als zwei Jahren und habe das Ninject-Wiki gelesen und verstanden. Ich benutze DI seitdem an vielen Orten. Hast du das gesehen Weniger als zwei Jahre und ein paar Seiten in einem Wiki lesen. Ich bin kein Zauberer oder so. Ich betrachte mich nicht als Genie, aber es war leicht für mich zu verstehen. Ich kann mir nicht vorstellen, dass jemand, der OO wirklich versteht, es nicht verstehen kann. Auf welche Handbücher mit 200 bis 300 Seiten beziehen Sie sich? Ich habe die noch nie gesehen. Alle IoC-Container, die ich verwendet habe, sind ziemlich kurz und auf den Punkt.
JR Garcia
35
+1 gut geschrieben und durchdacht. Eine Sache, von der ich denke, dass viele Leute nicht wissen, dass das Hinzufügen eines Frameworks oder dergleichen, um etwas "magisch" zu handhaben, einem System automatisch Komplexität und Einschränkungen hinzufügt. Manchmal lohnt es sich, aber oft wird etwas übersehen und Entropie wird in den Code freigesetzt, um ein durch Einschränkungen erzwungenes Problem zu beheben. Es mag im Voraus Zeit sparen, aber die Leute müssen sich darüber im Klaren sein, dass es 100-fach kosten kann, ein obskures Problem zu beheben. Nur eine Handvoll Leute haben wirklich genug Wissen, um es zu lösen.
kemiller2002
37

Vermutlich zwingt Sie niemand, ein DI-Container-Framework zu verwenden. Sie verwenden DI bereits, um Ihre Klassen zu entkoppeln und die Testbarkeit zu verbessern, sodass Sie viele der Vorteile erhalten. Kurz gesagt, Sie bevorzugen die Einfachheit, was im Allgemeinen eine gute Sache ist.

Wenn Ihr System einen Komplexitätsgrad erreicht, bei dem manuelle DI zur lästigen Pflicht wird (dh die Wartung erhöht), wägen Sie dies gegen die Team-Lernkurve eines DI-Container-Frameworks ab.

Wenn Sie mehr Kontrolle über die Verwaltung der Abhängigkeitslebensdauer benötigen (dh wenn Sie das Singleton-Muster implementieren möchten), sehen Sie sich DI-Container an.

Wenn Sie einen DI-Container verwenden, verwenden Sie nur die Funktionen, die Sie benötigen. Überspringen Sie die XML-Konfigurationsdatei und konfigurieren Sie sie im Code, wenn dies ausreicht. Halten Sie sich an die Konstruktorinjektion. Die Grundlagen von Unity oder StructureMap können auf einige Seiten reduziert werden.

Es gibt einen großartigen Blog-Beitrag von Mark Seemann dazu: Wann man einen DI-Container benutzt

TrueWill
quelle
Sehr gute Resonanz. Das einzige, worüber ich mich in meiner Meinung unterscheiden würde, ist, ob manuelle Injektion jemals als weniger komplex angesehen werden kann oder nicht.
wekempf
+1. Eine der besten Antworten hier. Danke auch für den Blog-Artikel-Link.
stakx - nicht mehr beitragen
1
+1 für den ausgeglichenen Standpunkt. IoC-Container sind nicht immer gut und nicht immer schlecht. Wenn Sie keinen Bedarf sehen, verwenden Sie ihn nicht. Sie müssen nur wissen, dass das Tool vorhanden ist, wenn Sie einen Bedarf sehen.
Phil
32

Meiner Meinung nach ist der Hauptvorteil eines IoC die Möglichkeit, die Konfiguration Ihrer Abhängigkeiten zu zentralisieren.

Wenn Sie derzeit die Abhängigkeitsinjektion verwenden, sieht Ihr Code möglicherweise so aus

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Wenn Sie eine statische IoC-Klasse verwenden, im Gegensatz zu den meiner Meinung nach verwirrenderen Konfigurationsdateien, könnten Sie Folgendes haben:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Dann würde Ihre statische IoC-Klasse so aussehen, ich verwende hier Unity.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}
Bendewey
quelle
11
Ben, Sie sprechen wirklich von Service Location, nicht von Dependency Injection - es gibt einen Unterschied. Ich bevorzuge immer das erste gegenüber dem letzteren. stevenharman.net/blog/archive/2009/09/25/…
stevenharman
23
Anstatt IoC.Resolve <T> in Ihrem Standardkonstruktor auszuführen, sollten Sie die Fähigkeit des IOC-Containers nutzen, das Objekt für Sie zu erstellen. Entfernen Sie diesen leeren Konstruktor und lassen Sie den IOC-Container das Objekt für Sie erstellen. var presenter = IoC.Resolve <CustomerPresenter> (); voila! Alle Abhängigkeiten sind bereits verkabelt.
Ben Scheirman
12
Der Nachteil der Zentralisierung dieser Art von Konfiguration ist die De-Kontextualisierung von Wissen. Joel weist auf dieses Problem hin, indem er sagt: "Code wird offen gesagt viel schwieriger zu lesen."
Scott Bellware
6
@sbellware, welches Wissen? Die Schnittstelle, die als ursprüngliche Abhängigkeit betrachtet wird, dient als Vertrag und sollte ausreichen, um sowohl den Zweck als auch das Verhalten zu vermitteln, nicht wahr? Ich nehme an, Sie beziehen sich möglicherweise auf Kenntnisse, die Sie bei der Umsetzung des Vertrags gewonnen haben. In welchem ​​Fall müssten Sie die entsprechende Konfiguration ermitteln? Ich verlasse mich dafür auf einen Computer (R # in VS, IntelliJ usw.), da sie sich hervorragend zum Navigieren in Knoten und Kanten eines Diagramms eignen. Ich reserviere den Stapel meines Gehirns für den Kontext der Arbeitsstelle, auf die ich mich gerade konzentriere.
Stevenharman
7
Steve, ich kenne den .NET-Text und bin viele Jahre auf diesem Weg gelaufen. Ich bin auch mit anderen Modi und Formen bestens vertraut und verstehe viszeral, dass es echte Kompromisse gibt. Ich weiß, wie und wann ich diese Kompromisse eingehen muss, aber die Vielfalt in meinem Arbeitsleben lässt mich zumindest Joels Perspektive greifbar verstehen. Erzählen Sie mir etwas, das ich nicht weiß, anstatt mich über Tools und Tricks zu unterrichten, die ich schon länger als die meisten .NET-Monokulturisten verwende. Ich reserviere den Stapel meines Gehirns eher für vielfältiges, kritisches Denken als für alt.net Stasis und Orthodoxie.
Scott Bellware
32

IoC-Container eignen sich auch zum Laden tief verschachtelter Klassenabhängigkeiten. Zum Beispiel, wenn Sie den folgenden Code mit Depedency Injection hatten.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Wenn Sie alle diese Abhängigkeiten in einen IoC-Container geladen haben, können Sie den CustomerService auflösen, und alle untergeordneten Abhängigkeiten werden automatisch aufgelöst.

Zum Beispiel:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}
Bendewey
quelle
Die IoC-Klasse wird jedoch mit dem Service Locator Anti-Pattern aufgerufen. Sollte_container mit dem Konstruktor DI übergeben werden, anstatt statisches T Resolve <T> () aufzurufen?
Michael Freidgeim
@bendewey Ihr Beispiel impliziert, dass die Argumente für den neuen CustomerService und das neue CustomerRepository automatisch an die Konstruktoren übergeben werden. Funktioniert das so? Was ist, wenn Sie auch andere zusätzliche Parameter haben?
Brain2000
28

Ich bin ein Fan von deklarativer Programmierung (sehen Sie sich an, wie viele SQL-Fragen ich beantworte), aber die IoC-Container, die ich mir angesehen habe, scheinen zu geheimnisvoll für ihr eigenes Wohl zu sein.

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

... oder beides ist bis zu dem einen oder anderen Grad wahr.

Ich denke nicht, dass das Konzept eines IoC-Containers schlecht ist. Die Implementierung muss jedoch sowohl leistungsstark (dh flexibel) genug sein, um in einer Vielzahl von Anwendungen nützlich zu sein, als auch einfach und leicht verständlich.

Es ist wahrscheinlich sechs von anderthalb Dutzend der anderen. Eine echte Anwendung (kein Spielzeug oder eine Demo) ist zwangsläufig komplex und berücksichtigt viele Eckfälle und Ausnahmen von den Regeln. Entweder kapseln Sie diese Komplexität in imperativen Code oder in deklarativen Code. Aber du musst es irgendwo darstellen.

Bill Karwin
quelle
5
Ich mag Ihre Beobachtung über das unvermeidliche Vorhandensein von Komplexität. Der Unterschied zwischen containergestützter und manueller Abhängigkeitsverdrahtung besteht darin, dass Sie sich bei Verwendung eines Containers auf jede Komponente einzeln konzentrieren und so die zunehmende Komplexität auf ziemlich lineare Weise verwalten können. Systeme mit manueller Abhängigkeitsverdrahtung sind schwieriger zu entwickeln, da die Konfiguration einer Komponente schwer von der Konfiguration aller anderen Komponenten zu isolieren ist.
Nicholas Blumhardt
Ich mag AspectJ. Es ist nicht Java, aber es ist auch nicht XML. Es ist eine Art IS Java, da es sich wie Java anfühlt und aussieht, indem es eine Erweiterung davon ist. Ich möchte meine Aspekte lieber aus meinen POJOs und meiner Geschäftslogik heraushalten. Ich würde fast gerne auch meine IoCs draußen lassen. IMHO enthält die Verkabelung von Tools viel zu viel XML. Wenn ich Konfigurationsdateien haben muss, lassen Sie sie BeanShell-Skripte sein. / Java, wende deine .Net-Workalikes hier an ->.
Chris K
2
Ich verstehe Ihre Antwort sicherlich, aber ich denke, ein großer Teil des Problems besteht darin, dass viele der IoC-Frameworks (und wirklich auch viele andere Frameworks für andere Dinge) für andere beschrieben und dokumentiert werden, die bereits sehr vertraut mit dem sind erforderliche Konzepte und Terminologie. Ich lerne viel darüber, nachdem ich gerade den Code eines anderen Programmierers geerbt habe. Ich habe Probleme zu verstehen, warum der Code IoC verwendet, wenn die 'Objektdiagramme' ziemlich klein sind und es keine echte Testabdeckung des Codes gibt. Als SQL-Programmierer können Sie die Verwendung von IoC mit einem ORM wahrscheinlich besser einschätzen.
Kenny Evitt
1
@KennyEvitt, guter Punkt, es scheint verfrüht, ein IoC-Framework zu verwenden, wenn man eine einfache App hat und sich nicht einmal die Mühe gemacht hat, eine gute Testabdeckung zu haben.
Bill Karwin
27

Bei der Verwendung eines Containers geht es hauptsächlich darum, von einem imperativen / skriptbasierten Initialisierungs- und Konfigurationsstil zu einem deklarativen zu wechseln . Dies kann verschiedene positive Auswirkungen haben:

  • Reduzierung der Startroutinen des Haarball- Hauptprogramms.
  • Aktivieren von ziemlich umfassenden Rekonfigurationsfunktionen für die Bereitstellungszeit.
  • Den Stil der Abhängigkeit injizierbaren zum Weg des geringsten Widerstands für neue Arbeiten machen.

Natürlich kann es Schwierigkeiten geben:

  • Code, der ein komplexes Start- / Herunterfahr- / Lebenszyklusmanagement erfordert, kann möglicherweise nicht einfach an einen Container angepasst werden.
  • Sie müssen wahrscheinlich alle persönlichen, prozess- und teamkulturellen Probleme lösen - aber deshalb haben Sie gefragt ...
  • Einige der Toolkits werden selbst schnell zu Schwergewichten, was die tiefe Abhängigkeit fördert, gegen die viele DI-Container als Gegenreaktion begannen.
Jeffrey Hantin
quelle
23

Für mich klingt es so, als hätten Sie bereits Ihren eigenen IoC-Container erstellt (unter Verwendung der verschiedenen von Martin Fowler beschriebenen Muster) und fragen, warum die Implementierung eines anderen besser ist als Ihre.

Sie haben also eine Menge Code, der bereits funktioniert. Und fragen sich, warum Sie es durch die Implementierung eines anderen ersetzen möchten.

Vorteile für die Prüfung eines IoC-Containers eines Drittanbieters

  • Sie erhalten Fehler kostenlos behoben
  • Das Bibliotheksdesign ist möglicherweise besser als deins
  • Personen sind möglicherweise bereits mit der jeweiligen Bibliothek vertraut
  • Die Bibliothek ist möglicherweise schneller als Ihre
  • Möglicherweise enthält es einige Funktionen, die Sie gerne implementiert hätten, für die Sie jedoch nie Zeit hatten (haben Sie einen Service Locater?).

Nachteile

  • Du bekommst kostenlos Fehler eingeführt :)
  • Das Bibliotheksdesign ist möglicherweise schlechter als das Ihre
  • Sie müssen eine neue API lernen
  • Zu viele Funktionen, die Sie niemals verwenden 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 Vorteile gegen Ihre Nachteile ab und treffen Sie eine Entscheidung.

Sam Safran
quelle
Ich stimme Ihnen zu. Sam - DI ist eine Art IoC. Wenn also das Originalplakat DI macht, machen sie bereits IoC. Die eigentliche Frage ist also, warum Sie Ihre eigenen rollen.
Jamie Love
3
Ich stimme dir nicht zu. IOC ist eine Möglichkeit, DI zu machen. IOC-Container führen DI durch, indem sie die Kontrolle über die Typinstanziierung mithilfe der dynamischen Laufzeitreflexion übernehmen, um Abhängigkeiten zu erfüllen und einzufügen. Das OP schlägt vor, dass er statische DI durchführt, und überlegt, warum er die Vorteile der Überprüfung der Kompilierungszeit gegen die Vorteile abwägen muss, die häufig mit der dynamischen Laufzeitreflexion des IOC verbunden sind.
Maxm007
17

Ich denke, der größte Teil des Werts eines IoC wird durch die Verwendung von DI erzielt. Da Sie dies bereits tun, ist der Rest des Vorteils inkrementell.

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

  • Bei mehreren Mandanten kann der IoC-Container einen Teil des Infrastrukturcodes zum Laden verschiedener Clientressourcen übernehmen. Wenn Sie eine Komponente benötigen, die für den Client spezifisch ist, verwenden Sie einen benutzerdefinierten Selektor, um die Logik zu verarbeiten, und sorgen Sie sich nicht um sie aus Ihrem Client-Code. Sie können dies sicherlich selbst erstellen, aber hier ist ein Beispiel dafür, wie ein IoC helfen kann.

  • Mit vielen Erweiterungspunkten kann der IoC zum Laden von Komponenten aus der Konfiguration verwendet werden. Dies ist eine übliche Sache, aber Werkzeuge werden vom Container bereitgestellt.

  • Wenn Sie AOP für einige Querschnittsthemen verwenden möchten, bietet das IoC Hooks zum Abfangen von Methodenaufrufen. Dies wird bei Projekten seltener ad-hoc durchgeführt, aber das IoC erleichtert es.

Ich habe bereits 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 von anderen erwähnt, können Sie auch zentral konfigurieren, welche Klassen Sie verwenden möchten. Dies kann zwar eine gute Sache sein, ist jedoch mit Fehlleitung und Komplikationen verbunden. Die Kernkomponenten für die meisten Anwendungen werden nicht viel ersetzt, so dass der Kompromiss etwas schwieriger 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 (dh Visualisierung des Kontrollflusses).

Steven Lyons
quelle
16

Ich bin ein genesender IOC-Süchtiger. Ich finde es heutzutage in den meisten Fällen schwierig, die Verwendung von IOC für DI zu rechtfertigen. IOC-Container opfern die Überprüfung der Kompilierungszeit und bieten Ihnen im Gegenzug eine "einfache" Einrichtung, eine komplexe Lebensdauerverwaltung und die sofortige Erkennung von Abhängigkeiten zur Laufzeit. Ich finde, dass der Verlust der Überprüfung der Kompilierungszeit und die daraus resultierenden Laufzeitmagien / Ausnahmen in den allermeisten Fällen die Schnickschnack nicht wert sind. In großen Unternehmensanwendungen können sie es sehr schwierig machen, den Vorgängen zu folgen.

Ich kaufe das Zentralisierungsargument nicht, weil Sie das statische Setup auch sehr einfach zentralisieren können, indem Sie eine abstrakte Factory für Ihre Anwendung verwenden und die Objekterstellung religiös auf die abstrakte Factory verschieben, dh die richtige DI ausführen.

Warum nicht statische magiefreie DI so machen:

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 Containerkonfiguration ist Ihre abstrakte Factory-Implementierung. Ihre Registrierungen sind Implementierungen von abstrakten Mitgliedern. Wenn Sie eine neue Singleton-Abhängigkeit benötigen, fügen Sie der abstrakten Factory einfach eine weitere abstrakte Eigenschaft hinzu. Wenn Sie eine vorübergehende Abhängigkeit benötigen, fügen Sie einfach eine andere Methode hinzu und fügen Sie sie als Func <> ein.

Vorteile:

  • Die gesamte Konfiguration für Einrichtung und Objekterstellung ist zentralisiert.
  • Konfiguration ist nur Code
  • Die Überprüfung der Kompilierungszeit erleichtert die Wartung, da Sie nicht vergessen können, Ihre Registrierungen zu aktualisieren.
  • Keine Laufzeitreflexionsmagie

Ich empfehle Skeptikern, das nächste Green Field-Projekt auszuprobieren und sich ehrlich zu fragen, an welchem ​​Punkt Sie den Container benötigen. Es ist einfach, einen IOC-Container später zu berücksichtigen, da Sie lediglich eine Factory-Implementierung durch ein IOC-Container-Konfigurationsmodul ersetzen.

Maxm007
quelle
Dies scheint mir kein Argument gegen IoC zu sein, sondern ein Argument gegen konfigurierbare IoC-Frameworks. Sie nicht Ihre Fabriken hier nur einkochen auf einfache hartcodierte IOCs?
Mr. Mindor
Ich denke, als das Poster IoC erwähnte, war ein IoC-Container-Framework gemeint. Vielen Dank für die ursprüngliche Beschreibung, die mir hilft, auf ein Framework zu verzichten. Wenn es also in Tests und im Produktionscode konfigurierbar ist, was ist der Vorteil eines "konfigurierbaren" Frameworks?
Huggie
Nach meinem Verständnis der Terminologie bedeutet Inversion of Control, Abhängigkeiten zur Laufzeit zu finden. Ja, man könnte sagen, die Fabrik ist im Grunde ein fest codierter oder statischer Container, aber es ist kein IOC-Container. Typen werden zur Kompilierungszeit aufgelöst. Es ist ein Argument gegen die Verwendung konfigurierbarer Frameworks, die Wörterbuchsuchen verwenden, um Typen zur Laufzeit aufzulösen.
Maxm007
14

Der größte Vorteil der Verwendung von IoC-Containern für mich (ich persönlich verwende Ninject) bestand darin, das Weitergeben von Einstellungen und anderen Arten von globalen Statusobjekten zu vermeiden.

Ich programmiere nicht für das Web, meine ist eine Konsolenanwendung und an vielen Stellen tief im Objektbaum muss ich Zugriff auf die vom Benutzer angegebenen Einstellungen oder Metadaten haben, die in einem vollständig separaten Zweig des Objektbaums erstellt werden. Mit IoC fordere ich Ninject einfach auf, die Einstellungen als Singleton zu behandeln (da es immer nur eine Instanz von ihnen gibt), die Einstellungen oder das Wörterbuch im Konstruktor anzufordern und vorab ... sie erscheinen magisch, wenn ich sie brauche!

Ohne Verwendung eines IoC-Containers müsste ich die Einstellungen und / oder Metadaten über 2, 3, ..., n Objekte weitergeben, bevor sie tatsächlich von dem Objekt verwendet werden, das sie benötigt.

DI / IoC-Container bieten viele weitere Vorteile, wie andere hier ausführlich beschrieben haben. Der Übergang von der Idee zum Erstellen von Objekten zum Anfordern von Objekten kann umwerfend sein. Die Verwendung von DI war jedoch für mich und mein Team sehr hilfreich, sodass Sie sie möglicherweise hinzufügen können zu deinem Arsenal!

Jeffrey Cameron
quelle
2
Das ist ein großes Plus. Ich kann nicht glauben, dass es noch niemand zuvor erwähnt hat. Ohne die Notwendigkeit, temporäre Objekte zu übergeben oder zu speichern, wird der Code viel sauberer.
Finglas
1
@qble globale Objekte funktionieren hervorragend ... wenn Sie möchten, dass Ihre Codebasis ein eng gekoppelter, nicht testbarer Albtraum ist, der mit der Zeit immer schwieriger zu ändern ist. viel Glück damit.
Jeffrey Cameron
2
@JeffreyCameron IoC Container ist ein globales Objekt. Ihre globalen Abhängigkeiten werden nicht entfernt.
Qble
3
Der IoC-Container ist jedoch für den Rest der Anwendung transparent. Sie rufen es beim Start einmal auf, um eine Instanz zu erhalten, und das ist das Letzte, was Sie davon sehen. Mit globalen Objekten ist Ihr Code mit harten Abhängigkeiten übersät
Jeffrey Cameron
1
@qble verwendet Fabriken, um vorübergehende Instanzen im laufenden Betrieb zu erstellen. Die Fabriken sind Singleton-Dienste. Wenn die transienten Instanzen etwas aus dem IOC-Container benötigen, verbinden Sie sie mit der Factory und veranlassen Sie die Factory, die Abhängigkeit an die transiente Instanz zu übergeben.
Jeffrey Cameron
14

IoC-Frameworks eignen sich hervorragend, wenn Sie ...

  • ... Sicherheit wegwerfen. Viele (alle?) IoC-Frameworks zwingen Sie, den Code auszuführen, wenn Sie sicher sein möchten, dass alles korrekt angeschlossen ist. "Hey! Ich hoffe, ich habe alles eingerichtet, damit meine Initialisierungen dieser 100 Klassen in der Produktion nicht fehlschlagen und Nullzeiger-Ausnahmen auslösen!"

  • ... Wurf Code mit Globals (IoC Frameworks sind alle globalen Zustände zu ändern).

  • ... beschissenen Code mit unklaren Abhängigkeiten schreiben, der schwer zu überarbeiten ist, da Sie nie wissen, was von was abhängt.

Das Problem mit IoC ist, dass die Leute, die sie verwenden, Code wie diesen geschrieben haben

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

Das ist offensichtlich fehlerhaft, da die Abhängigkeit zwischen Foo und Bar fest verdrahtet ist. Dann erkannten sie, dass es besser wäre, Code wie zu schreiben

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

das ist auch fehlerhaft, aber weniger offensichtlich. In Haskell wäre die Art von Foo(), IO Fooaber Sie wollen das IO-part wirklich nicht und es sollte ein Warnzeichen sein, dass etwas mit Ihrem Design nicht stimmt, wenn Sie es haben.

Um es loszuwerden (den IO-Teil), nutzen Sie alle Vorteile von IoC-Frameworks und keinen seiner Nachteile. Sie könnten stattdessen eine abstrakte Factory verwenden.

Die richtige Lösung wäre so etwas wie

data Foo = Foo { apa :: Bar }

oder vielleicht

data Foo = forall b. (IBar b) => Foo { apa :: b }

und injizieren (aber ich würde es nicht injizieren nennen) Bar.

Außerdem: Sehen Sie sich dieses Video mit Erik Meijer (Erfinder von LINQ) an, in dem er sagt, dass DI für Leute ist, die keine Mathematikkenntnisse haben (und ich könnte nicht mehr zustimmen): http://www.youtube.com/watch?v = 8Mttjyf-8P4

Im Gegensatz zu Mr. Spolsky glaube ich nicht, dass Leute, die IoC-Frameworks verwenden, sehr klug sind - ich glaube einfach, dass sie keine Mathematikkenntnisse haben.

finnsson
quelle
9
Typensicherheit wegwerfen - außer dass Ihr Beispiel noch typsicher ist, ist das die Schnittstelle. Sie geben keine Objekttypen weiter, sondern den Schnittstellentyp. Und die Typensicherheit beseitigt ohnehin keine Nullreferenzausnahmen. Sie können gerne Nullreferenzen als typsichere Parameter übergeben - das ist überhaupt kein Typensicherheitsproblem.
Blowdart
Sie werfen die Typensicherheit weg, da Sie nicht wissen, ob der Typ im IoC-Container verkabelt ist oder nicht (der Typ von IoC <IBar> ist IBares tatsächlich nicht IO IBar). Und ja, im Haskell-Beispiel Ich kann keine Nullreferenz bekommen.
Finnsson
Bei der Typensicherheit geht es nicht wirklich darum, dass ein Objekt verkabelt wird, zumindest nicht in Java / C #. Es geht einfach darum, dass das Objekt vom richtigen Typ ist. Und ConcreteClass myClass = null ist immer noch vom richtigen Typ. Wenn Sie nicht null erzwingen möchten, kommen Codeverträge ins Spiel.
Blowdart
2
Ich stimme Ihrem ersten Punkt in gewissem Maße zu. Ich hätte gerne eine Erklärung für den zweiten und den dritten Punkt, der für mich nie ein Problem war. Ihr C # -Beispiel verwendet den IoC-Container als Service-Locator, ein häufiger Fehler. Und C # gegen Haskell = Äpfel gegen Orangen vergleichen.
Mauricio Scheffer
2
Sie werfen die Typensicherheit nicht weg. Bei einigen (wenn nicht den meisten) DI-Containern können Sie das vollständige Objektdiagramm nach dem Registrierungsprozess überprüfen. Auf diese Weise können Sie verhindern, dass die Anwendung bei einer Fehlkonfiguration startet (schnell fehlschlägt), und meine Anwendungen verfügen immer über einige Komponententests, die die Produktionskonfiguration aufrufen, damit ich diese Fehler während des Tests finden kann. Ich würde jedem empfehlen, der einen IoC-Container verwendet, einige Unit-Tests zu schreiben, die sicherstellen, dass alle Objekte der obersten Ebene aufgelöst werden können.
Steven
10

Ich habe festgestellt, dass die korrekte Implementierung von Dependency Injection Programmierer dazu zwingt, eine Vielzahl anderer Programmierpraktiken zu verwenden, die dazu beitragen, die Testbarkeit, Flexibilität, Wartbarkeit und Skalierbarkeit von Code zu verbessern: Praktiken wie das Prinzip der Einzelverantwortung, Trennung von Bedenken und Codierung gegen APIs. Es fühlt sich so an, als ob ich gezwungen wäre, modularere Klassen und Methoden in Bissgröße zu schreiben, was das Lesen des Codes erleichtert, da er in mundgerechten Blöcken aufgenommen werden kann.

Es werden jedoch auch ziemlich große Abhängigkeitsbäume erstellt, die über ein Framework (insbesondere wenn Sie Konventionen verwenden) viel einfacher verwaltet werden können als von Hand. Heute wollte ich etwas sehr schnell in LINQPad testen, und ich dachte, es wäre zu mühsam, einen Kernel zu erstellen und meine Module zu laden, und am Ende schrieb ich dies von Hand:

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

Rückblickend wäre es schneller gewesen, das IoC-Framework zu verwenden, da die Module so ziemlich alles durch Konvention definieren.

Nachdem ich einige Zeit damit verbracht habe, die Antworten und Kommentare zu dieser Frage zu studieren, bin ich überzeugt, dass die Leute, die gegen die Verwendung eines IoC-Containers sind, keine echte Abhängigkeitsinjektion praktizieren. Die Beispiele, die ich gesehen habe, beziehen sich auf Praktiken, die häufig mit der Abhängigkeitsinjektion verwechselt werden. Einige Leute beschweren sich über Schwierigkeiten beim "Lesen" des Codes. Bei korrekter Ausführung sollte der überwiegende Teil Ihres Codes bei Verwendung von DI von Hand identisch sein wie bei Verwendung eines IoC-Containers. Der Unterschied sollte vollständig in einigen "Startpunkten" innerhalb der Anwendung liegen.

Mit anderen Worten, wenn Sie IoC-Container nicht mögen, führen Sie Dependency Injection wahrscheinlich nicht so durch, wie es gemacht werden soll.

Ein weiterer Punkt: Die Abhängigkeitsinjektion kann wirklich nicht von Hand durchgeführt werden, wenn Sie irgendwo Reflexion verwenden. Ich hasse es zwar, was Reflexion für die Code-Navigation bedeutet, aber Sie müssen erkennen, dass es bestimmte Bereiche gibt, in denen dies wirklich nicht vermieden werden kann. ASP.NET MVC versucht beispielsweise, den Controller bei jeder Anforderung durch Reflektion zu instanziieren. Um die Abhängigkeitsinjektion von Hand durchzuführen, müssten Sie jeden Controller wie folgt zu einem "Kontextstamm" machen:

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

Vergleichen Sie dies nun damit, dass ein DI-Framework dies für Sie tun kann:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

Beachten Sie bei Verwendung eines DI-Frameworks Folgendes:

  • Ich kann diese Klasse einem Unit-Test unterziehen. Durch das Erstellen eines Modells ISimpleWorkflowInstanceMergerkann ich testen, ob es wie erwartet verwendet wird, ohne dass eine Datenbankverbindung oder ähnliches erforderlich ist.
  • Ich verwende viel weniger Code und der Code ist viel einfacher zu lesen.
  • Wenn sich eine der Abhängigkeiten meiner Abhängigkeit ändert, muss ich keine Änderungen am Controller vornehmen. Dies ist besonders hilfreich, wenn Sie bedenken, dass mehrere Controller wahrscheinlich dieselben Abhängigkeiten verwenden.
  • Ich verweise niemals explizit auf Klassen aus meiner Datenschicht. Meine Webanwendung kann nur einen Verweis auf das Projekt enthalten, das die ISimpleWorkflowInstanceMergerSchnittstelle enthält. Auf diese Weise kann ich die Anwendung in separate Module aufteilen und eine echte mehrschichtige Architektur beibehalten, was wiederum die Flexibilität der Dinge erheblich erhöht.

Eine typische Webanwendung verfügt über einige Controller. Der ganze Schmerz, DI von Hand in jedem Controller auszuführen, wird sich wirklich summieren, wenn Ihre Anwendung wächst. Wenn Sie eine Anwendung mit nur einem Kontextstamm haben, die niemals versucht, einen Dienst durch Reflektion zu instanziieren, ist dies kein so großes Problem. Trotzdem wird die Verwaltung von Anwendungen, die Dependency Injection verwenden, ab Erreichen einer bestimmten Größe extrem teuer, es sei denn, Sie verwenden ein Framework zum Verwalten des Abhängigkeitsdiagramms.

StriplingWarrior
quelle
9

Immer wenn Sie das Schlüsselwort "new" verwenden, erstellen Sie eine konkrete Klassenabhängigkeit, und in Ihrem Kopf sollte eine kleine Alarmglocke ertönen. Es wird schwieriger, dieses Objekt isoliert zu testen. Die Lösung besteht darin, auf Schnittstellen zu programmieren und die Abhängigkeit zu injizieren, damit das Objekt mit allem, was diese Schnittstelle implementiert (z. B. Mocks), einem Unit-Test unterzogen werden kann.

Das Problem ist, dass Sie irgendwo Objekte konstruieren müssen. Ein Factory-Muster ist eine Möglichkeit, die Kopplung aus Ihren POXOs zu verschieben (Plain Old-Objekte "Fügen Sie hier Ihre OO-Sprache ein"). Wenn Sie und Ihre Mitarbeiter alle Code wie diesen schreiben, ist ein IoC-Container die nächste "inkrementelle Verbesserung", die Sie an Ihrer Codebasis vornehmen können. Es wird all diesen fiesen Factory-Boilerplate-Code aus Ihren sauberen Objekten und Ihrer Geschäftslogik herausschieben. Sie werden es bekommen und lieben. Geben Sie einem Unternehmen einen Vortrag darüber, warum Sie es lieben, und begeistern Sie alle.

Wenn Ihre Mitarbeiter noch keine DI durchführen, sollten Sie sich zuerst darauf konzentrieren. Verbreiten Sie das Wort darüber, wie man sauberen Code schreibt, der leicht zu testen ist. Sauberer DI-Code ist der schwierige Teil. Wenn Sie dort sind, sollte es relativ trivial sein, die Objektverdrahtungslogik von Factory-Klassen in einen IoC-Container zu verschieben.

matte Verbrennungen
quelle
8

Da alle Abhängigkeiten deutlich sichtbar sind, werden Komponenten erstellt, die lose miteinander verbunden und gleichzeitig für die gesamte Anwendung leicht zugänglich und wiederverwendbar sind.

bbmud
quelle
7

Sie nicht brauchen einen IoC - Container.

Wenn Sie jedoch konsequent einem DI-Muster folgen, werden Sie feststellen, dass mit einem solchen eine Menge redundanten, langweiligen Codes entfernt werden.

Dies ist ohnehin oft die beste Zeit, um eine Bibliothek / ein Framework zu verwenden - wenn Sie verstehen, was es tut und es ohne die Bibliothek tun könnten.

Kyoryu
quelle
6

Ich bin gerade dabei, selbst entwickelten DI-Code herauszuziehen und durch ein IOC zu ersetzen. Ich habe wahrscheinlich weit über 200 Codezeilen entfernt und durch etwa 10 ersetzt. Ja, ich musste ein wenig lernen, wie man den Container verwendet (Winsor), aber ich bin Ingenieur und arbeite an Internet-Technologien in der 21. Jahrhundert, also bin ich daran gewöhnt. Ich habe wahrscheinlich ungefähr 20 Minuten damit verbracht, mir die Anleitungen anzusehen. Das war meine Zeit wert.

Matt Wrock
quelle
3
"Aber ich bin ein Ingenieur, der im 21. Jahrhundert an Internet-Technologien arbeitet, also bin ich daran
gewöhnt
5

Während Sie Ihre Klassen weiter entkoppeln und Ihre Abhängigkeiten invertieren, bleiben die Klassen weiterhin klein und das "Abhängigkeitsdiagramm" wird immer größer. (Dies ist nicht schlecht.) Die Verwendung der Grundfunktionen eines IoC-Containers macht die Verkabelung all dieser Objekte trivial, aber die manuelle Ausführung kann sehr mühsam werden. Was ist zum Beispiel, wenn ich eine neue Instanz von "Foo" erstellen möchte, aber eine "Leiste" benötigt? Und ein "Balken" braucht ein "A", "B" und "C". Und jeder von ihnen braucht 3 andere Dinge usw. usw. (ja, ich kann mir keine guten falschen Namen einfallen lassen :)).

Die Verwendung eines IoC-Containers zum Erstellen Ihres Objektdiagramms für Sie reduziert die Komplexität um eine Tonne und überträgt es in eine einmalige Konfiguration. Ich sage einfach "Erstelle mir ein 'Foo'" und es findet heraus, was benötigt wird, um eines zu bauen.

Einige Leute verwenden die IoC-Container für viel mehr Infrastruktur, was für fortgeschrittene Szenarien in Ordnung ist, aber in diesen Fällen stimme ich zu, dass sie Code verschleiern und das Lesen und Debuggen für neue Entwickler erschweren können.

Aaron Lerch
quelle
1
Warum beschwichtigen wir weiterhin den kleinsten gemeinsamen Nenner, anstatt daran zu arbeiten, sie hochzuziehen?
Stevenharman
4
Ich habe nichts über Beschwichtigung gesagt. Ich habe gerade gesagt, dass die fortgeschrittenen Szenarien die Dinge für neue Entwickler verschleiern können - das ist eine Tatsache. Ich habe nicht gesagt "also tu es nicht". Aber selbst dann (Zeit für den Anwalt des Teufels) gibt es oft andere Möglichkeiten, dasselbe zu erreichen, die eine Codebasis expliziter und zugänglicher machen. Das Verbergen der Komplexität in der benutzerdefinierten Konfiguration Ihres Infrastruktur-Tools, nur weil Sie dies können, ist nicht der richtige Grund und zieht niemanden hoch.
Aaron Lerch
4

Das Gleiche gilt für die Einheit. Wenn Sie zu groß werden, können Sie das Knarren in den Sparren hören.

Es überrascht mich nie, wenn Leute anfangen darüber zu reden, wie sauber IoC-Code aussieht, wie die gleichen Leute, die einmal darüber gesprochen haben, wie Vorlagen in C ++ in den 90er Jahren der elegante Weg waren, sie heutzutage als arkan zu entschlüsseln . Bah!

M Lopez
quelle
2

In der .NET-Welt ist AOP nicht allzu beliebt, daher ist für DI ein Framework Ihre einzige echte Option, unabhängig davon, ob Sie eines selbst schreiben oder ein anderes Framework verwenden.

Wenn Sie AOP verwendet haben, können Sie diese beim Kompilieren Ihrer Anwendung injizieren, was in Java häufiger vorkommt.

DI bietet viele Vorteile, z. B. eine reduzierte Kopplung, sodass das Testen von Einheiten einfacher ist. Wie werden Sie es jedoch implementieren? Möchten Sie Reflexion verwenden, um es selbst zu tun?

James Black
quelle
Das neue MEF-Framework ermöglicht genau dies für .NET und ich muss sagen, dass der Code sauberer und verständlicher ist und das Verständnis des DI-Konzepts erheblich erleichtert.
Terjetyl
4
@TT: MEF ist kein Abhängigkeitsinjektionscontainer und hat nichts mit AOP zu tun.
Mauricio Scheffer
2

Es sind also fast 3 Jahre vergangen, oder?

  1. 50% derjenigen, die sich für IoC-Frameworks ausgesprochen haben, verstehen den Unterschied zwischen IoC- und IoC-Frameworks nicht. Ich bezweifle, dass sie wissen, dass Sie Code schreiben können, ohne auf einem App-Server bereitgestellt zu werden

  2. Wenn wir das beliebteste Java Spring-Framework verwenden, wird die IoC-Konfiguration von XMl in den Code verschoben und sieht nun so aus

`@Configuration öffentliche Klasse AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `Und wir brauchen einen Rahmen, um dies zu tun, warum genau?

Ivan
quelle
1

Ehrlich gesagt finde ich nicht, dass es viele Fälle gibt, in denen IoC-Container benötigt werden, und meistens fügen sie nur unnötige Komplexität hinzu.

Wenn Sie es nur verwenden, um die Konstruktion eines Objekts zu vereinfachen, muss ich fragen, ob Sie dieses Objekt an mehr als einer Stelle instanziieren. Würde ein Singleton nicht Ihren Bedürfnissen entsprechen? Ändern Sie die Konfiguration zur Laufzeit? (Wechseln der Datenquellentypen usw.).

Wenn ja, benötigen Sie möglicherweise einen IoC-Container. Wenn nicht, verschieben Sie die Initialisierung nur von der Stelle, an der der Entwickler sie leicht sehen kann.

Wer hat gesagt, dass eine Schnittstelle sowieso besser ist als Vererbung? Angenommen, Sie testen einen Dienst. Warum nicht den Konstruktor DI verwenden und mithilfe der Vererbung Verspottungen der Abhängigkeiten erstellen? Die meisten Dienste, die ich benutze, haben nur wenige Abhängigkeiten. Doing Unit - Tests auf diese Weise verhindert , dass eine Tonne nutzlos Schnittstellen beibehalten und bedeutet , dass Sie nicht haben zu ReSharper verwenden , um schnell die Deklaration einer Methode zu finden.

Ich glaube, dass es für die meisten Implementierungen ein Mythos ist, zu sagen, dass IoC-Container nicht benötigten Code entfernen.

Zuerst wird der Container eingerichtet. Dann müssen Sie noch jedes Objekt definieren, das initialisiert werden muss. Sie speichern also keinen Code bei der Initialisierung, sondern verschieben ihn (es sei denn, Ihr Objekt wird mehrmals verwendet. Ist es besser als Singleton?). Anschließend müssen Sie für jedes Objekt, das Sie auf diese Weise initialisiert haben, eine Schnittstelle erstellen und verwalten.

Hat jemand irgendwelche Gedanken dazu?

Fred
quelle
Selbst für kleine Projekte macht das Einfügen der Konfiguration in XML sie so viel sauberer und modularer - und macht Code zum ersten Mal wiederverwendbar (da er nicht mehr mit Klebstoff kontaminiert ist). Für geeignete Softwareprodukte können Sie beispielsweise ShippingCostCalculatoroder ProductUIPresentationeine andere Strategie / Implementierung innerhalb genau derselben Codebasis konfigurieren oder austauschen , indem Sie nur eine geänderte XML-Datei bereitstellen.
Thomas W
Wenn es als allgemeine Praxis wohl oder übel implementiert wird, fügen Sie meiner Meinung nach mehr Komplexität hinzu, nicht weniger. Wenn Sie zur Laufzeit etwas austauschen müssen, sind sie großartig, aber warum sollten Sie die Verschleierung hinzufügen? Warum sollten Sie den Overhead der zusätzlichen Schnittstellen für Dinge behalten, die niemals ausgeschaltet werden? Ich sage nicht, dass Sie IoC nicht zum Testen verwenden. Verwenden Sie auf jeden Fall die Eigenschafts- oder sogar Konstruktorabhängigkeitsinjektion. Benötigen Sie eine Schnittstelle? Warum nicht Ihre Testobjekte unterordnen? Wäre das nicht sauberer?
Fred
Sie benötigen überhaupt keine Schnittstellen, um XML-Konfiguration / IoC durchzuführen. In einem kleinen Projekt mit nur ~ 8 Seiten und 5 oder 6 Diensten habe ich große Verbesserungen in der Klarheit festgestellt. Es gibt weniger Verschleierung, da Dienste und Controller keinen Klebercode mehr benötigen.
Thomas W
1

Sie benötigen einen IoC-Container, wenn Sie die Konfiguration Ihrer Abhängigkeiten zentralisieren müssen, damit sie problemlos in großen Mengen ausgetauscht werden können. Dies ist am sinnvollsten bei TDD, wo viele Abhängigkeiten ausgetauscht werden, die Abhängigkeiten jedoch nur wenig voneinander abhängig sind. Dies geschieht auf Kosten einer Verschleierung des Kontrollflusses der Objektkonstruktion bis zu einem gewissen Grad. Daher ist eine gut organisierte und angemessen dokumentierte Konfiguration wichtig. Es ist auch gut, einen Grund dafür zu haben, sonst ist es nur eine Vergoldung der Abstraktion . Ich habe gesehen, dass es so schlecht gemacht wurde, dass es heruntergezogen wurde, um einer goto-Anweisung für Konstruktoren zu entsprechen.

DaveMorganTexas
quelle
1

Hier ist warum. Das Projekt heißt IOC-with-Ninject. Sie können es mit Visual Studio herunterladen und ausführen. In diesem Beispiel wird Ninject verwendet, aber ALLE 'neuen' Anweisungen befinden sich an einem Speicherort. Sie können die Ausführung Ihrer Anwendung vollständig ändern, indem Sie das zu verwendende Bindungsmodul ändern. Das Beispiel ist so eingerichtet, dass Sie an eine verspottete Version der Dienste oder an die reale Version binden können. Bei kleinen Projekten spielt das vielleicht keine Rolle, aber bei großen Projekten ist es eine große Sache.

Um ganz klar zu sein, Vorteile, wie ich sie sehe: 1) ALLE neuen Anweisungen an einer Stelle im Stammverzeichnis des Codes. 2) Code mit einer Änderung komplett neu faktorisieren. 3) Extrapunkte für 'Cool Factor', weil es ... gut: cool ist. : p

CodeChops
quelle
1

Ich werde versuchen herauszufinden, warum das IOC aus meiner Sicht möglicherweise nicht gut ist.

Wie bei allem anderen ist der IOC-Container (oder wie Einstein es ausdrücken würde, I = OC ^ 2) ein Konzept, das Sie selbst entscheiden müssen, ob Sie es in Ihrem Code benötigen oder nicht. Der jüngste Aufschrei der Mode über IOC ist nur das, Mode. Verlieben Sie sich nicht in Mode, das ist das Erste. Es gibt unzählige Konzepte, die Sie in Ihren Code implementieren können. Zunächst verwende ich die Abhängigkeitsinjektion, seit ich mit dem Programmieren begonnen habe, und habe den Begriff selbst gelernt, als er unter diesem Namen populär gemacht wurde. Die Abhängigkeitskontrolle ist ein sehr altes Thema und wurde bisher auf Billionen von Wegen behandelt, je nachdem, was von was entkoppelt wurde. Alles von allem zu entkoppeln ist ein Unsinn. Das Problem mit dem IOC-Container besteht darin, dass er versucht, genauso nützlich zu sein wie Entity Framework oder NHibernate. Während das Schreiben eines objektrelationalen Mappers einfach ein Muss ist, sobald Sie eine Datenbank mit Ihrem System koppeln müssen, ist ein IOC-Container nicht immer erforderlich. Also, wenn IOC-Container nützlich ist:

  1. Wenn Sie eine Situation mit vielen Abhängigkeiten haben, die Sie organisieren möchten
  2. Wenn Sie Ihren Code nicht mit einem Produkt eines Drittanbieters koppeln möchten
  3. Wenn Ihre Entwickler lernen möchten, wie man mit einem neuen Tool arbeitet

1: Es kommt nicht so oft vor, dass Sie so viele Abhängigkeiten in Ihrem Code haben oder dass Sie diese schon früh im Design kennen. Abstraktes Denken ist nützlich, wenn abstraktes Denken fällig ist.

2: Das Koppeln Ihres Codes mit Code von Drittanbietern ist ein HuGe-Problem. Ich habe mit Code gearbeitet, der mehr als 10 Jahre alt ist und der damals ausgefallenen und fortgeschrittenen Konzepten wie ATL, COM, COM + und so weiter folgte. Mit diesem Code können Sie jetzt nichts mehr tun. Was ich damit sagen will ist, dass ein fortschrittliches Konzept einen offensichtlichen Vorteil bietet, der jedoch langfristig mit dem veralteten Vorteil selbst aufgehoben wird. Es hatte einfach alles teurer gemacht.

3: Softwareentwicklung ist schwer genug. Sie können es auf nicht erkennbare Ebenen erweitern, wenn Sie zulassen, dass ein erweitertes Konzept in Ihren Code eingefügt wird. Es gibt ein Problem mit IOC2. Obwohl es Abhängigkeiten entkoppelt, entkoppelt es auch den Logikfluss. Stellen Sie sich vor, Sie haben einen Fehler gefunden und müssen eine Pause einlegen, um die Situation zu untersuchen. IOC2 macht dies wie jedes andere fortschrittliche Konzept schwieriger. Das Beheben eines Fehlers innerhalb eines Konzepts ist schwieriger als das Beheben eines Fehlers in einem einfacheren Code, da beim Beheben eines Fehlers ein Konzept erneut befolgt werden muss. (Um nur ein Beispiel zu nennen: C ++ .NET ändert die Syntax ständig so stark, dass Sie gründlich überlegen müssen, bevor Sie eine ältere Version von .NET überarbeiten.) Wo liegt also das Problem mit IOC? Das Problem besteht in der Lösung von Abhängigkeiten. Die Logik zum Auflösen ist üblicherweise im IOC2 selbst verborgen. geschrieben vielleicht auf ungewöhnliche Weise, die Sie lernen und pflegen müssen. Wird Ihr Drittanbieterprodukt in 5 Jahren verfügbar sein? Microsoft war nicht.

Das "Wir wissen wie" -Syndrom ist in Bezug auf IOC2 allgegenwärtig. Dies ähnelt dem Testen der Automatisierung. Ausgefallene Laufzeit und perfekte Lösung auf den ersten Blick: Sie lassen einfach alle Ihre Tests über Nacht ausführen und sehen die Ergebnisse am Morgen. Es ist wirklich schmerzhaft, Unternehmen für Unternehmen zu erklären, was automatisierte Tests wirklich bedeuten. Automatisierte Tests sind definitiv kein schneller Weg, um die Anzahl der Fehler zu reduzieren, die Sie über Nacht einführen können, um die Qualität Ihres Produkts zu verbessern. Aber die Mode macht diesen Begriff ärgerlich dominant. IOC2 leidet unter dem gleichen Syndrom. Es wird angenommen, dass Sie es implementieren müssen, damit Ihre Software gut ist. Jedes kürzlich geführte Interview wurde mich gefragt, ob ich IOC2 und Automatisierung implementiere. Das ist ein Zeichen der Mode: Das Unternehmen hat einen Teil des Codes in MFC geschrieben, den sie nicht aufgeben werden.

Sie müssen IOC2 wie jedes andere Konzept in Software lernen. Die Entscheidung, ob IOC2 verwendet werden muss, liegt im Team und im Unternehmen. Mindestens ALLE oben genannten Argumente müssen jedoch erwähnt werden, bevor die Entscheidung getroffen wird. Nur wenn Sie sehen, dass die Plus-Seite die negative Seite überwiegt, können Sie eine positive Entscheidung treffen.

An IOC2 ist nichts auszusetzen, außer dass es nur die Probleme löst, die es löst, und die Probleme einführt, die es einführt. Nichts anderes. Es ist jedoch sehr schwierig, gegen die Mode vorzugehen, sie haben einen schweißtreibenden Mund, die Anhänger von allem. Es ist seltsam, wie keiner von ihnen da ist, wenn das Problem mit ihrer Phantasie offensichtlich wird. Viele Konzepte in der Softwareindustrie wurden verteidigt, weil sie Gewinn bringen, Bücher geschrieben, Konferenzen abgehalten und neue Produkte hergestellt werden. Das ist Mode, normalerweise von kurzer Dauer. Sobald Menschen etwas anderes finden, geben sie es vollständig auf. IOC2 ist nützlich, zeigt aber die gleichen Anzeichen wie viele andere verschwundene Konzepte, die ich gesehen habe. Ich weiß nicht, ob es überleben wird. Dafür gibt es keine Regel. Sie denken, wenn es nützlich ist, wird es überleben. Nein, so geht es nicht. Ein großes, reiches Unternehmen reicht aus und das Konzept kann innerhalb weniger Wochen sterben. Wir werden sehen. NHibernate überlebte, EF wurde Zweiter. Vielleicht überlebt IOC2 auch. Vergessen Sie nicht, dass die meisten Konzepte in der Softwareentwicklung nichts Besonderes sind, sie sind sehr logisch, einfach und offensichtlich, und manchmal ist es schwieriger, sich an die aktuelle Namenskonvention zu erinnern, als das Konzept selbst zu verstehen. Macht das Wissen über IOC2 einen Entwickler zu einem besseren Entwickler? Nein, denn wenn ein Entwickler nicht in der Lage war, ein Konzept zu entwickeln, das IOC2 ähnelt, ist es für ihn schwierig zu verstehen, welches Problem IOC2 löst. Die Verwendung sieht künstlich aus und er oder sie beginnt möglicherweise damit um politisch korrekt zu sein. Vergessen Sie nicht, dass die meisten Konzepte in der Softwareentwicklung nichts Besonderes sind, sie sind sehr logisch, einfach und offensichtlich, und manchmal ist es schwieriger, sich an die aktuelle Namenskonvention zu erinnern, als das Konzept selbst zu verstehen. Macht das Wissen über IOC2 einen Entwickler zu einem besseren Entwickler? Nein, denn wenn ein Entwickler nicht in der Lage war, ein Konzept zu entwickeln, das IOC2 ähnelt, ist es für ihn schwierig zu verstehen, welches Problem IOC2 löst. Die Verwendung sieht künstlich aus und er oder sie beginnt möglicherweise damit um politisch korrekt zu sein. Vergessen Sie nicht, dass die meisten Konzepte in der Softwareentwicklung nichts Besonderes sind, sie sind sehr logisch, einfach und offensichtlich, und manchmal ist es schwieriger, sich an die aktuelle Namenskonvention zu erinnern, als das Konzept selbst zu verstehen. Macht das Wissen über IOC2 einen Entwickler zu einem besseren Entwickler? Nein, denn wenn ein Entwickler nicht in der Lage war, ein Konzept zu entwickeln, das IOC2 ähnelt, ist es für ihn schwierig zu verstehen, welches Problem IOC2 löst. Die Verwendung sieht künstlich aus und er oder sie beginnt möglicherweise damit um politisch korrekt zu sein. Macht das Wissen über IOC2 einen Entwickler zu einem besseren Entwickler? Nein, denn wenn ein Entwickler nicht in der Lage war, ein Konzept zu entwickeln, das IOC2 ähnelt, ist es für ihn schwierig zu verstehen, welches Problem IOC2 löst. Die Verwendung sieht künstlich aus und er oder sie beginnt möglicherweise damit um politisch korrekt zu sein. Macht das Wissen über IOC2 einen Entwickler zu einem besseren Entwickler? Nein, denn wenn ein Entwickler nicht in der Lage war, ein Konzept zu entwickeln, das IOC2 ähnelt, ist es für ihn schwierig zu verstehen, welches Problem IOC2 löst. Die Verwendung sieht künstlich aus und er oder sie beginnt möglicherweise damit um politisch korrekt zu sein.

user1228608
quelle
0

Persönlich verwende ich IoC als eine Art Strukturkarte meiner Anwendung (Ja, ich bevorzuge auch StructureMap;)). Es macht es einfach, meine üblichen Schnittstellenimplementierungen während der Tests durch Moq-Implementierungen zu ersetzen. Das Erstellen eines Test-Setups kann so einfach sein wie ein neuer Init-Aufruf meines IoC-Frameworks, bei dem die Klasse, die meine Testgrenze ist, durch ein Mock ersetzt wird.

Dies ist wahrscheinlich nicht das, wofür IoC da ist, aber es ist das, wofür ich es am meisten benutze.

cwap
quelle
0

Mir ist klar, dass dies ein ziemlich alter Beitrag ist, aber er scheint immer noch einigermaßen aktiv zu sein, und ich dachte, ich würde ein paar Punkte beisteuern, die in anderen Antworten noch nicht erwähnt wurden.

Ich werde sagen, dass ich mit den Vorteilen der Abhängigkeitsinjektion einverstanden bin, aber ich ziehe es vor, die Objekte selbst zu konstruieren und zu verwalten, wobei ich ein Muster verwende, das dem von Maxm007 in seiner Antwort beschriebenen nicht unähnlich ist. Ich habe zwei Hauptprobleme bei der Verwendung von Containern von Drittanbietern festgestellt:

1) Wenn eine Bibliothek eines Drittanbieters die Lebensdauer Ihrer Objekte "automatisch" verwaltet, kann dies zu unerwarteten Ergebnissen führen. Wir haben festgestellt, dass Sie insbesondere in großen Projekten erheblich mehr Kopien eines Objekts haben können, als Sie erwarten, und mehr als wenn Sie die Lebenszyklen manuell verwalten würden. Ich bin sicher, dass dies je nach verwendetem Framework unterschiedlich ist, aber das Problem besteht trotzdem. Dies kann auch problematisch sein, wenn Ihr Objekt Ressourcen, Datenverbindungen usw. enthält, da das Objekt manchmal länger als erwartet leben kann. Daher erhöhen IoC-Container zwangsläufig die Ressourcennutzung und den Speicherbedarf einer Anwendung.

2) IoC-Container sind meiner Meinung nach eine Form der "Black-Box-Programmierung". Ich habe festgestellt, dass insbesondere unsere weniger erfahrenen Entwickler dazu neigen, sie zu missbrauchen. Es ermöglicht dem Programmierer, nicht darüber nachdenken zu müssen, wie Objekte zueinander in Beziehung stehen sollen oder wie sie entkoppelt werden sollen, da es ihnen einen Mechanismus bietet, mit dem sie einfach jedes gewünschte Objekt aus der Luft greifen können. Beispielsweise kann es einen guten Entwurfsgrund geben, dass ObjectA niemals direkt über ObjectB Bescheid wissen sollte, aber anstatt eine Fabrik, eine Brücke oder einen Service-Locator zu erstellen, sagt ein unerfahrener Programmierer einfach: "Kein Problem, ich hole ObjectB einfach aus dem IoC-Container." ". Dies kann tatsächlich zu einer erhöhten Objektkopplung führen, was IoC verhindern soll.

Amnesie
quelle
-8

Die Abhängigkeitsinjektion in einem ASP.NET-Projekt kann mit wenigen Codezeilen durchgeführt werden. Ich nehme an, dass die Verwendung eines Containers einen gewissen Vorteil hat, wenn Sie eine App haben, die mehrere Frontends verwendet und Unit-Tests benötigt.

etechpartner
quelle
1
Kannst du ein Beispiel geben? Wie unterscheidet sich DI in ASP.NET von anderen Anwendungstypen?
tofi9