Die derzeit am besten bewertete Antwort auf eine sehr aktuelle Frage besagt dies
DI-Container sind ein "Unternehmenssoftware" -Muster, das verwendet wird, wenn das Objektdiagramm sehr groß und komplex ist. Ich vermute, dass 95% der Anwendungen dies nicht erfordern.
Dem stimme ich überhaupt nicht zu. Vielleicht habe ich die Terminologie falsch verstanden, aber für mich bedeutet DI-Framework nur "etwas, das meine Objekte miteinander verbindet". Vermisse ich etwas
Ich benutze Guice sogar für wirklich kleine Projekte (wie 10 Klassen), um sie zu vereinfachen . Sicher, es ist eine 400-kB-JAR-Datei, aber das ist mir egal. Für ein kleines Projekt brauche ich kaum eine Konfiguration und der einzige "Overhead" ist das Hinzufügen der @Inject
Anmerkung.
Ich frage mich also wirklich, welche zusätzliche Komplexität DI-Frameworks verursachen.
Update Adressierung der Antworten
In einem 82-Klassen-Projekt habe ich
- 32
@Inject
Anmerkungen - 15
@Singleton
und 1@ProvidedBy
Anmerkungen - 4 Anbieter (alle würde ich auch ohne DI brauchen, da sie meine Fabriken sind)
- 1 Modul mit einer einzelnen Zeile
- 0 Zeilen XML !!!
Das ist alles. Sicher, es ist ein kleines Projekt, aber genau das war mein Punkt. Die zusätzliche Arbeit bestand eher aus ein paar Worten als aus Zeilen .
Ich verwende ausschließlich Konstruktorinjektion, um unveränderliche "unbeschwerte" Objekte zu erhalten. Immer wenn eine neue Abhängigkeit auftritt, füge ich ein letztes Feld hinzu, lasse Lomboks RequiredArgsConstructor sich um die Deklaration kümmern und lasse Guice sich darum kümmern, sie ordnungsgemäß aufzurufen.
quelle
Antworten:
Soweit ich sehen kann, müssen bei der Verwendung von DI-Frameworks einige Kompromisse eingegangen werden.
Das Besorgniserregendste für mich ist, dass Ihr Anwendungscode normalerweise zwischen einer Hauptprogrammiersprache (wie Java) und XML / JSON-Konfigurationscode (es ist Code) verteilt ist. Dies bedeutet, dass Sie bei Problemen mit Ihrer Anwendung an zwei Stellen suchen müssen. Oft ist der Konfigurationscode nicht leicht mit dem Hauptanwendungscode zu verknüpfen.
Natürlich liegt die Konfiguration auch außerhalb der Grenzen dessen, was der Compiler (und häufig die IDE) für Sie überprüfen kann, was bedeutet, dass es viel einfacher ist, Fehler in dieser Konfiguration zu machen, als wenn Sie eine Hauptklasse schreiben, die die Verkabelung handhabt. Tatsächlich bedeutet dies, dass Verkabelungsprobleme von Problemen zur Kompilierungszeit zu Laufzeitproblemen werden.
Neben der Aufteilung der Anwendung bedeutet die Verwendung von DI-Frameworks häufig auch, dass Sie
@Inject
anstelle herkömmlicher DI-Techniken (CTOR-Schnittstelleninjektion in Java / C ++, Vorlageninjektion in C ++) innerhalb Ihres Codes oder ähnliches verwenden. Die Kehrseite der Medaille ist , dass Sie müssen dann einen DI - Framework mit der Anwendung verwenden. Eine Alternative wäre, Ihre Anwendung zu entwerfen, ohne ein DI-Framework zu erwarten, und dann dem DI-Framework zu erlauben, die traditionelle CTOR / Setter-Injektion Ihrer Klassen wiederzuverwenden.Der Nachteil des herkömmlichen Injektionsmechanismus besteht darin, dass eine komplexe und ordnungsgemäß eingekapselte Klasse zur CTOR-Zeit eine Reihe anderer relativ komplexer Injektionen erfordert. Dies wird normalerweise durch Einarbeiten von a gelöst
Builder
, aber es kann ein Schmerz sein.BEARBEITEN :
Die Jungs unten haben erwähnt, dass
Guice
keine separate XML / JSON-Konfiguration erforderlich ist. Meine Antwort bezieht sich wirklich auf meine eigene Verwendung von Spring.quelle
Ich denke, der Hauptnachteil der Verwendung eines IoC-Containers ist die Magie, die er hinter den Kulissen ausübt. (Dies ist auch ein Problem bei ORMs, einem weiteren gängigen magischen Tool.) Das Debuggen von IoC-Problemen macht keinen Spaß, da es schwieriger wird zu sehen, wie und wann Abhängigkeiten erfüllt werden. Sie können die Abhängigkeitsauflösung im Debugger nicht schrittweise durchlaufen.
Container machen es oft umständlich, "Klasse A mit einer Version einer Abhängigkeit und Klasse B mit einer anderen Version" oder ähnliche Szenarien zu konfigurieren, in denen Abstraktionen wiederverwendet werden. Sie können in Situationen geraten, in denen jede Klasse ihre eigene Schnittstelle hat, auch wenn sie dasselbe bedeuten. Dies blockiert Sie von der Abstraktionskraft der Wiederverwendung von Schnittstellen.
Meiner Meinung nach ist der automatische Gewinn, den Sie mit IoC erzielen, das automatische Lifestyle-Management. Sie können Komponenten als Singletons, Singletons pro Anforderung, transiente Instanzen usw. deklarieren - und der Container erledigt alles für Sie. Es ist erheblich schwieriger, dies mit
new
-expressions einzurichten . Auf der anderen Seite ist es immer noch leicht, Ressourcenlecks zu verursachen, indem Lebensstile nicht übereinstimmen.Einige Projekte verwenden IoC, um alles im System - sogar Klassen, die Geschäftsdaten darstellen - ohne
new
Schlüsselwörter zu erstellen . Ich denke, dies ist im Allgemeinen ein Fehler, da es Sie dazu ermutigt, schlecht gekapselte Klassen und zu viele Schnittstellen zu schreiben. Ihr Code wird abhängig von IoC. Es scheint auch komorbid mit Übertests zu sein.Ich persönlich ziehe es im Allgemeinen vor, auf einen Container zu verzichten. Stattdessen komponiere ich Objekte mit
new
-expressions, die in einem Kompositionsstamm eingekapselt sind. Es ist leicht, die konkreten Abhängigkeiten einer bestimmten Klasse zu erkennen, und wenn ich einen Konstruktor ändere, erhalte ich Fehler bei der Kompilierung, die beide große Wartungsgewinne bedeuten. Es ist auch einfach, separate Instanzen mit unterschiedlichen Komponenten zu konfigurieren.Mein Rat, wenn Sie einen IoC-Container verwenden möchten, ist, Ihre Abhängigkeiten am Einstiegspunkt in das System zu erfüllen und sie nur für Ihre tatsächlichen Abhängigkeiten zu verwenden (Repositorys et al.). Denken Sie daran, dass Sie die Abhängigkeitsinjektion weiterhin verwenden und die Konfiguration sogar in einer einzelnen Datei beibehalten können, ohne einen Container zu verwenden.
quelle
Einige Entwickler argumentieren, dass jedes Framework, das Sie in Ihrem Code verwenden, zu einem späteren Zeitpunkt ein potenzielles Problem darstellt, da Sie normalerweise Ihren Code auf eine bestimmte Weise schreiben müssen, um korrekt mit ihm zu interagieren. Dies kann zu einem späteren Zeitpunkt zu Problemen führen Es stellt sich heraus, dass diese Methoden im Widerspruch zu anderen Designanforderungen stehen. Ein Beispiel für diese Sorge wird von Bob Martin ausgedrückt hier . Ich persönlich stimme nicht zu, aber ich kann seinen Standpunkt sehen.
quelle
Zusammenfassend lässt sich sagen, dass die einzige Komplexität darin besteht, falsche Konfigurationsmethoden zu verwenden (ja, XML ist nicht typsicher) und möglicherweise auch aus Frameworks, die viel mehr Arbeit leisten (Spring verwaltet den Objektlebenszyklus, der weit über DI hinausgeht).
Für ein einfaches Projekt ist es äußerst nützlich, DI richtig zu machen:
Es ist auch ziemlich harmlos, da alles manuell gemacht werden könnte. Es geht nur darum, die Instanzen zu erstellen, sie an Konstruktoren zu übergeben, um mehr Instanzen zu erstellen, und so weiter. Langweilig und lang, aber einfach.
Ein typischer Service mit Guice und Lombok sieht so aus
Wenn Sie nun eine Abhängigkeit hinzufügen, müssen Sie lediglich ein neues privates Endfeld hinzufügen . Es kann nicht null sein (Guice garantiert dies auch ohne @NonNull ).
Testen bedeutet, entweder eigene Instanzen zu erstellen oder diese von der
Injector
zu beziehen (möglicherweise so konfiguriert, dass einige Stubs zurückgegeben werden). Beides ist ziemlich einfach.quelle