Ich entschuldige mich, wenn dies wie eine weitere Wiederholung der Frage erscheint, aber jedes Mal, wenn ich einen Artikel zum Thema finde, wird meist nur darüber gesprochen, was DI ist. Also, ich bekomme DI, aber ich versuche zu verstehen, dass ein IoC-Container benötigt wird, in den sich offenbar jeder hineinzuversetzen scheint. Ist der Sinn eines IoC-Containers wirklich nur, die konkrete Implementierung der Abhängigkeiten "automatisch aufzulösen"? Vielleicht haben meine Klassen nicht viele Abhängigkeiten, und vielleicht sehe ich deshalb nicht die große Sache, aber ich möchte sicherstellen, dass ich den Nutzen des Containers richtig verstehe.
Normalerweise teile ich meine Geschäftslogik in eine Klasse auf, die ungefähr so aussieht:
public class SomeBusinessOperation
{
private readonly IDataRepository _repository;
public SomeBusinessOperation(IDataRespository repository = null)
{
_repository = repository ?? new ConcreteRepository();
}
public SomeType Run(SomeRequestType request)
{
// do work...
var results = _repository.GetThings(request);
return results;
}
}
Es hat also nur die eine Abhängigkeit, und in einigen Fällen hat es vielleicht eine zweite oder dritte, aber nicht allzu oft. So kann alles, was dies aufruft, sein eigenes Repo übergeben oder es erlauben, das Standard-Repo zu verwenden.
Nach meinem derzeitigen Verständnis eines IoC-Containers ist alles, was der Container tut, die Auflösung von IDataRepository. Aber wenn das alles ist, sehe ich nicht viel Wert darin, da meine Operationsklassen bereits einen Fallback definieren, wenn keine Abhängigkeit übergeben wird. Der einzige andere Vorteil, den ich mir vorstellen kann, ist, dass ich mehrere Operationen wie habe Wenn Sie dasselbe Fallback-Repo verwenden, kann ich dieses Repo an einer Stelle ändern, die die Registrierung / Fabrik / Container ist. Und das ist toll, aber ist es das?
quelle
ConcreteRepository
und (2) Sie können zusätzliche Abhängigkeiten für angebenConcreteRepository
(eine Datenbankverbindung wäre beispielsweise üblich).Antworten:
Im IoC-Container geht es nicht um den Fall, dass Sie eine Abhängigkeit haben. Es geht um den Fall, dass Sie 3 Abhängigkeiten haben und diese mehrere Abhängigkeiten haben, die Abhängigkeiten usw. haben.
Sie können damit auch die Auflösung einer Abhängigkeit zentralisieren und den Lebenszyklus von Abhängigkeiten verwalten.
quelle
Es gibt eine Reihe von Gründen, warum Sie einen IoC-Container verwenden möchten.
Nicht referenzierte DLLs
Sie können einen IoC-Container verwenden, um eine konkrete Klasse aus einer nicht referenzierten DLL aufzulösen. Dies bedeutet, dass Sie Abhängigkeiten vollständig von der Abstraktion - dh der Schnittstelle - übernehmen können.
Vermeiden Sie die Verwendung von
new
Ein IoC-Container bedeutet, dass Sie die Verwendung des
new
Schlüsselworts zum Erstellen einer Klasse vollständig entfernen können . Dies hat zwei Auswirkungen. Das erste ist, dass es Ihre Klassen entkoppelt. Das zweite (was damit zusammenhängt) ist, dass Sie Mocks für Unit-Tests einwerfen können. Dies ist unglaublich nützlich, insbesondere wenn Sie mit einem lang laufenden Prozess interagieren.Schreiben Sie gegen Abstraktionen
Wenn Sie einen IoC-Container verwenden, um Ihre konkreten Abhängigkeiten für Sie aufzulösen, können Sie Ihren Code gegen Abstraktionen schreiben, anstatt jede konkrete Klasse nach Bedarf zu implementieren. Möglicherweise benötigen Sie Ihren Code, um Daten aus einer Datenbank zu lesen. Anstatt die Datenbank-Interaktionsklasse zu schreiben, schreiben Sie einfach eine Schnittstelle dafür und codieren dagegen. Sie können einen Mock verwenden, um die Funktionalität des Codes zu testen, den Sie während der Entwicklung entwickeln, anstatt sich auf die Entwicklung der konkreten Datenbankinteraktionsklasse zu verlassen, bevor Sie den anderen Code testen können.
Vermeiden Sie spröden Code
Ein weiterer Grund für die Verwendung eines IoC-Containers besteht darin, dass Sie nicht jeden einzelnen Aufruf eines Klassenkonstruktors ändern müssen, wenn Sie eine Abhängigkeit hinzufügen oder entfernen, indem Sie sich auf den IoC-Container verlassen, um Ihre Abhängigkeiten aufzulösen. Der IoC-Container löst Ihre Abhängigkeiten automatisch auf. Dies ist kein großes Problem, wenn Sie eine Klasse einmal erstellen, aber ein riesiges Problem, wenn Sie die Klasse an hundert Orten erstellen.
Lebensdauerverwaltung und nicht verwaltete Ressourcenbereinigung
Der letzte Grund, den ich erwähne, ist die Verwaltung der Objektlebensdauer. IoC-Container bieten häufig die Möglichkeit, die Lebensdauer eines Objekts anzugeben. Es ist sehr sinnvoll, die Lebensdauer eines Objekts in einem IoC-Container anzugeben, anstatt zu versuchen, es manuell im Code zu verwalten. Das manuelle Lebensdauermanagement kann sehr schwierig sein. Dies kann nützlich sein, wenn Sie mit Objekten arbeiten, die entsorgt werden müssen. Anstatt die Entsorgung Ihrer Objekte manuell zu verwalten, verwalten einige IoC-Container die Entsorgung für Sie, was dazu beitragen kann, Speicherverluste zu vermeiden und Ihre Codebasis zu vereinfachen.
Das Problem mit dem von Ihnen bereitgestellten Beispielcode besteht darin, dass die von Ihnen geschriebene Klasse eine konkrete Abhängigkeit von der ConcreteRepository-Klasse aufweist. Ein IoC-Container würde diese Abhängigkeit entfernen.
quelle
Nach dem Prinzip der Einzelverantwortung darf jede Klasse nur eine einzige Verantwortung haben. Das Erstellen neuer Instanzen von Klassen ist nur eine weitere Aufgabe. Sie müssen diese Art von Code in eine oder mehrere Klassen einschließen. Sie können dazu beliebige Muster verwenden, z. B. Fabriken, Builder, DI-Container usw.
Es gibt andere Prinzipien wie die Inversion der Kontrolle und die Inversion der Abhängigkeit. In diesem Zusammenhang beziehen sie sich auf die Instanziierung von Abhängigkeiten. Sie geben an, dass die High-Level-Klassen von den von ihnen verwendeten Low-Level-Klassen (Abhängigkeiten) entkoppelt werden müssen. Wir können Dinge entkoppeln, indem wir Schnittstellen schaffen. Daher müssen die Klassen auf niedriger Ebene spezifische Schnittstellen implementieren, und die Klassen auf hoher Ebene müssen Instanzen von Klassen verwenden, die diese Schnittstellen implementieren. (Hinweis: Die einheitliche REST-Schnittstellenbeschränkung wendet denselben Ansatz auf Systemebene an.)
Die Kombination dieser Prinzipien anhand eines Beispiels (Entschuldigung für den Code mit niedriger Qualität, ich habe eine Ad-hoc-Sprache anstelle von C # verwendet, da ich das nicht weiß):
Kein SRP, kein IoC
Näher an SRP, kein IoC
Ja SRP, nein IoC
Ja SRP, ja IoC
So entstand ein Code, in dem Sie jede konkrete Implementierung durch eine andere ersetzen können, die dieselbe Schnittstelle ofc implementiert. Das ist gut so, denn die teilnehmenden Klassen sind voneinander entkoppelt, sie kennen nur die Schnittstellen. Ein weiterer Vorteil ist, dass der Code der Instanziierung wiederverwendbar ist.
quelle