2010-10-19 9 views
13

Ich betrachte mich selbst als erfahrener Programmierer und verstehe das Grundkonzept der Abhängigkeitsinjektion. Auf der anderen Seite, die meiste meiner Erfahrung ist schriftlich relativ Low-Level, Ein-Mann-Nummer-Crunching-Code. Ich habe keinerlei Erfahrung in großen Unternehmensprojekten.Dependency Injection Frameworks: Wie funktionieren sie?

Angesichts dieses Hintergrunds kann ich nicht für das Leben von mir meinen Kopf darum herum wickeln, warum irgendjemand einen Rahmen brauchen würde, um Abhängigkeitsinjektion zu machen. Kann mir jemand einen kurzen Überblick über die Funktionsweise eines solchen Frameworks geben, ohne auf viele Details einzugehen und zu erklären, wie es das Leben leichter macht, als nur das eigene zu rollen?

Edit: Ich habe hier einige gute Antworten bekommen. Habe ich Recht, wenn ich sage, dass ein DI-Framework im Grunde eine bequeme Möglichkeit bietet, global zugängliche Fabriken zu erstellen, die Abhängigkeiten erzeugen/zurückgeben, wann immer ein Objekt sie anfordert? Wenn ja, habe ich die ganze Zeit auf diese Art und Weise sehr ad hoc in meinem Code gearbeitet, aber ich habe nie daran gedacht, irgendein formelles/schweres Framework dafür zu verwenden.

Antwort

7

Bei DI dreht sich alles um die Entkopplung von Abhängigkeiten zwischen Klassen.

Mit DI haben Ihre Klassen keine Verweise auf implementierende Klassen mehr.Das Fabrikmuster kommt dem DI nahe, aber es ist anders, weil eine Fabrikklasse selbst eine ungewollte Abhängigkeit ist (die zum Beispiel den Komponententest verletzen wird).

DI ist auch nicht unbedingt eine globale oder anwendungsweite Angelegenheit; Es ist möglich, verschiedene Abhängigkeitsschemas für dieselbe Anwendung und dieselben Klassen zu konfigurieren. Es kann sogar Laufzeitabhängigkeiten geben. DI kann sogar den Lebenszyklus oder den Bereich bestimmen, in dem Objekte leben müssen (Anforderungsumfang, Sitzungsumfang usw.).

Es ist auch nicht schwer oder aufdringlich, wenn Sie leichte DI-Implementierungen wie Google’s Guice betrachten. Nicht umsonst nennen sie es das neue "Neue".

+0

Ich akzeptiere das, weil es meine Frage beantwortet. Ich habe jedoch ein wenig über Guice gesucht und gelesen und bin immer noch skeptisch, ob es wirklich einfacher oder weniger spröde ist, es zu konfigurieren, als nur ein paar Factory Functions/Klassen zu schreiben und/oder ein paar Konfigurationsflags in der entsprechenden Weise zu speichern Du versuchst es zu erreichen. – dsimcha

+0

Ich sehe es als eine Verbesserung dieser Muster, da es auch die Abhängigkeit von den Fabrikklassen löst. Es ist auch weniger Codierung, mit einer einfachen Anmerkung erstellen Sie ganze Fabriken. Also weniger Bugs. – Kdeveloper

+0

Je mehr ich darüber nachdenke, desto mehr denke ich, dass ein DI-Framework praktisch sein könnte. Es wäre etwas, das ich verwenden würde, wenn es in der Standard-Lib wäre, und würde das Leben marginal einfacher machen, aber nicht etwas, womit ich die Abhängigkeiten von Dritten stören würde. Meine Hauptsprache ist D, ich bin ein Entwickler für seine Standardbibliothek, und die Metaprogrammierungsmöglichkeiten von D sind ziemlich gut, also denke ich halbwegs daran, ein sehr einfaches, leichtgewichtiges (wie in einem Modul) DI-Framework zu schreiben D Standardbibliothek – dsimcha

5

Wikipedia hat eine gute Übersicht über Vorteile und Implementierungen.

http://en.wikipedia.org/wiki/Inversion_of_control

Ich finde IoC Container wie Spring hilfreich sein, wegen all der anderen „Stoff“ sie tun, wie Transaktionsmanagement und der Code, den sie bieten, wie Code-Vorlagen, Test-Basisklassen, usw., die sind sehr nützlich. Außerdem denke ich, dass IoC in der Java-Welt eine Reaktion auf einen Java-EE-Standard ist, den viele Entwickler als zu klobig empfanden. IoC, wie es von Spring implementiert wurde, ermöglicht es Ihnen, POJO-Servicekomponenten größtenteils zu entwickeln, was meiner Meinung nach einfacher ist als die Hoops, durch die Java EE die Leute zum Springen brachte.

Ich viel davon, oder zumindest einige davon, ist mentale Masturbation, zu einem gewissen Grad, was bedeutet, dass die IoC-Vorteile durch die Container in den meisten Projekten, die sie verwenden, nicht voll genutzt werden.
"Wir sollten DI verwenden, weil wir die Implementierungen unserer DAOs in Zukunft einfach austauschen können, yippeeeeee" - das passiert meiner Erfahrung nach selten. Leute benutzen sie, weil sie zu einem Standard geworden sind, und Schlagwort IMHO. Ich sage nicht, dass das schlechte Dinge sind; nur, dass der IoC-Teil der Standard-Software nicht wirklich der Grund ist, warum sie verwendet werden.

3

Ich mag @hvgotcodes Antwort, aber dachte ID fügen Sie ein paar Punkte, als ich ein großer Fan von DI/IoC.

Wir verwenden DI, für die folgenden Vorteile:

  1. Testbarkeit. Wir schreiben Unit-Tests gegen ein Repository (zum Beispiel) über Schnittstellen. Für Integrationstests verwenden wir jetzt DI, um ein "echtes" Repository für unsere Tests zu erstellen. Aber was ist mit Komponententests? Wir wollen nicht das "echte" Repository/die Datenbank testen. Also verwenden wir DI, um ein Mock-Repository zu "injizieren". Eine Menge davon hat mit der Magie von Schnittstellen zu tun (natürlich könnten Sie Ihre Tests "fest codieren", um das gefälschte Repository zu verwenden, wenn es nötig ist).

  2. Zentraler Injektionspunkt. - AKA-Registrierung. Jedes Projekt/Modul hat eine Registry, in der wir Komponenten ein- und ausschalten. Wenn zum Beispiel in unserem Testprojekt "etwas" ein IR-Repository anfordert, injizieren wir ein MockRepository. Wenn "etwas" ein IRepository anfordert, injizieren wir in unserem Service-Layer-Projekt ein EntityFrameworkRepository.

  3. Ausgezeichnete Scope Management. Dies hängt vom DI-Container ab (wir verwenden StructureMap für .NET).Sie können Ihre Objekte jedoch so einrichten, dass Singleton-, HTTP-Context- und ThreadLocal-Lebensdauern in einer einzelnen Zeile enthalten sind, wenn Sie Ihre Registrierung einrichten. Das ist wieder nett, weil Sie die Objekte an einem zentralen Ort behandeln. Ihre Komponenten haben keine Ahnung von ihrer Lebensdauer, sie können sich auf ihre Arbeit konzentrieren und sonst nichts.

  4. Ein-Zeilen-Umschalten der Komponenten. Dies wird durch die Registrierung ermöglicht (Punkt # 2). Im Moment verwenden wir Entity Framework für Persistenz. Das einzige, was darüber "weiß", ist die Registry. Wenn eines Tages etwas Besseres herauskommt, müssen wir lediglich eine weitere Implementierung von IRepository erstellen und über eine Zeile in der Registry blättern. Dies ist in langlebigen Anwendungen sehr gut möglich. Wir haben L2SQL für v1 unserer Website verwendet, jetzt verwenden wir EF für v2. Würden wir DI mit v1 verwenden, würde sich die Umstellungszeit fast halbieren. Aber alles ist an die Persistenzschicht gebunden, also schreiben wir die Website grundsätzlich von Grund auf neu.

Die Vorteile von DI/IoC sind wirklich, wenn Sie eine Menge verschiedenen Prinzipien wie Repository, POCO, DDD, Schnittstellen, N-Tier kombinieren gesehen.

Ich würde es nicht für eine kleine Anwendung verwenden, die sich nicht entwickeln wird.

Das sind meine zwei Cent.

+0

Die Elemente 1 und 4 sind eine Funktion der Abhängigkeitsinjektion, nicht von Containern, worum das OP sich interessiert. –

+0

@Mauricio Scheffer - ein verwandtes Thema, meinst du nicht? Ich sehe die Frage nach Dependency Injection, nicht nur "warum sind DI-Container gut". – RPM1984

2

Wenn Sie die DI-Route starten, haben Sie ziemlich bald Constrictors, die 20 Parameter für die verschiedenen Abhängigkeiten nehmen, die das Objekt benötigt. Um diese 20 Parameter zu erhalten, müssen 20 andere Parameter erstellt werden. Und dann, am Ende (Anfang?) Von allem, werden Sie feststellen, dass Sie sich gerade an eine konkrete Implementierung Ihrer sorgfältig durchdachten Schnittstelle gekoppelt haben (indem Sie den Konstruktor aufrufen). Und dann, 6 Monate später, fügen Sie eine neue Abhängigkeit hinzu, bei der Sie alle vorhandenen Aufrufe zurückverfolgen und diese ebenfalls ändern müssen.

Ein DI-Framework kümmert sich grundsätzlich um diese Rohrleitungen für Sie. Indem er zwischen Ihnen und dem Konstruktor steht, kann er config (vielleicht XML, vielleicht Code) abfragen, der ihm sagt, was zu tun ist, wenn er ein konkretes Objekt benötigt.

Die Grundlagen sind ziemlich einfach mit jeder Sprache, die eine Art von Introspektion hat (registrieren Sie einen konkreten Typ, um eine Schnittstelle zu erfüllen, wenn Sie eine Instanz einer Schnittstelle benötigen, dann instanziieren Sie den Typ. Gehen Sie das vom Konstruktor erstellte Diagramm und wiederholen .) wo es kompliziert wird, wenn Sie auch Objektlebensdauern steuern wollen (diese Logger-Klasse sollte nur einmal instanziiert und dann wiederverwendet werden. Diese DataAccessFacade sollte einmal pro Thread instanziiert werden usw.) oder dynamisch auswählen, wie a Abhängigkeit. Ein DI-Framework stellt normalerweise sowohl Objekterstellungsfunktionen (zum Ermitteln der für den Konstruktor erforderlichen Abhängigkeiten) als auch eine Service Locator-Einrichtung bereit, so dass nicht alles im Konstruktor oder als Parameter für Methoden übergeben werden muss. Dadurch können Sie Typen mit beliebig komplexen Konstruktoren registrieren, eine Instanz davon abrufen und sich keine Gedanken darüber machen, wann oder wie Sie eine Instanz teilen, wann sie konstruiert, wann sie zerstört oder an den tatsächlichen Typ gekoppelt werden.

Das Erstellen von Bibliotheken mit DI ermöglicht es Benutzern auch, Abhängigkeiten bei Bedarf auszutauschen - dies bietet eine große Flexibilität in Verbindung mit Schnittstellen oder Vererbung, ohne den Code mit Konstruktor- oder Methodenargumenten für jede Abhängigkeit zu überladen.

+0

Haben Sie schon einmal eine ausgelagerte Implementierung gesehen? – hvgotcodes

+0

@hvgotcodes - Sicher. Die offensichtlichen sind zum Testen oder zum Loggen. Hardwarefassaden, Datenzugriff, 2-stufige bis 3-stufige Komponenten usw. Ich will damit nicht sagen, dass Sie sich von allen zukünftigen Eventualitäten isolieren oder entwerfen können - aber Sie können den Schmerz sicherlich lindern. –