5

Nachdem ich die netten Antworten in this question gelesen hatte, sah ich die Screencasts von Justin Etheredge. Es scheint alles sehr nett, mit einem Minimum an Setup erhalten Sie DI direkt aus Ihrem Code.Warum möchten Sie Dependency Injection ohne Konfiguration?

Jetzt ist die Frage, die mir vorkommt: Warum möchten Sie ein DI-Framework verwenden, das keine Konfigurationsdateien verwendet? Ist das nicht der Sinn einer DI-Infrastruktur, um das Verhalten (sozusagen die "Strategie") nach dem Erstellen/Freigeben/wie auch immer den Code zu verändern?

Kann mir jemand einen guten Anwendungsfall geben, der eine nicht konfigurierte DI wie Ninject validiert?

Antwort

10

Ich glaube nicht, dass Sie ein DI-Framework ohne Konfiguration möchten. Ich denke du willst ein DI-Framework mit der Konfiguration brauchst du.

Ich nehme den Frühling als Beispiel. In den "alten Tagen" haben wir alles in XML-Dateien gespeichert, um alles konfigurierbar zu machen.

Wenn Sie in ein vollständig annotiertes System wechseln, definieren Sie grundsätzlich, welche Komponente Rollen Ihre Anwendung enthält. So kann ein gegebener Service beispielsweise eine Implementierung haben, die für "reguläre Laufzeit" steht, wo es eine andere Implementierung gibt, die in der "Stubbed" -Version der Anwendung gehört. Bei der Verdrahtung für Integrationstests verwenden Sie möglicherweise eine dritte Implementierung.

Wenn Sie das Problem auf diese Weise betrachten, werden Sie schnell feststellen, dass die meisten Anwendungen nur eine sehr begrenzte Anzahl von Komponentenrollen in der Laufzeit enthalten - das sind die Dinge, die tatsächlich dazu führen, dass verschiedene Versionen einer Komponente verwendet werden. Und normalerweise eine gegebene Implementierung einer Komponente ist immer gebunden an diese Rolle; Es ist wirklich der Existenzgrund dieser Implementierung.

Wenn Sie also die "Konfiguration" einfach angeben, welche Komponentenrollen Sie benötigen, können Sie ohne viel mehr Konfiguration wegkommen. Natürlich wird es immer Ausnahmen geben, aber dann behandeln Sie nur die Ausnahmen.

+0

aber was ist mit keiner Konfiguration (wie in Google Guice) - wie geben Sie die "Komponentenrollen"? – JohnIdol

+0

Das letzte Mal, als ich Guice angeschaut habe, fehlte der Aspekt "convention over configuration", den man durch das Mischen von Annotationen, XML und Rollen erhalten konnte, was auch der Grund ist, warum ich Spring javaconfig nicht verwende. Ich bevorzuge das gemischte Paradigma. Das Ersetzen von XML durch Code, der das Gleiche tut, schneidet mir nichts an, vielleicht ist es nur, dass ich engstirnig bin, aber ich kann nicht sehen, wie Code-basierte Konfiguration für mich einen Mehrwert schaffen würde. Aber ich weiß nicht, ob Sie ein Convention-basiertes Setup in Guice erreichen können. – krosenvold

0

Wenn Sie das Verhalten nach einem Release-Build ändern möchten, benötigen Sie ein DI-Framework, das externe Konfigurationen unterstützt, ja.

Aber ich denke an andere Szenarien, in denen diese Konfiguration nicht notwendig ist: zum Beispiel steuern Sie die Injektion der Komponenten in Ihrer Geschäftslogik. Oder verwenden Sie ein DI-Framework, um das Testen von Einheiten zu vereinfachen.

1

Sie müssen nicht einmal ein DI-Framework verwenden, um das Abhängigkeitsinjektionsmuster anzuwenden. Sie können einfach statische Factory-Methoden zum Erstellen Ihrer Objekte verwenden, wenn Sie abgesehen von der Neukompilierung von Code keine Konfigurierbarkeit benötigen.

Es hängt also davon ab, wie konfigurierbar Sie Ihre Anwendung sein soll. Wenn Sie möchten, dass es ohne Neukompilierung des Codes konfigurierbar/steckbar ist, benötigen Sie etwas, das Sie über Text- oder XML-Dateien konfigurieren können.

0

Sie sollten über PRISM in .NET lesen (es ist Best Practices, Composite-Anwendungen in .NET zu tun). In diesen Best Practices "expose" jedes Modul ihren Implementierungstyp in einem gemeinsamen Container. Auf diese Weise hat jedes Modul eine klare Verantwortung dafür, "wer die Implementierung für diese Schnittstelle bereitstellt".Ich denke, es wird klar genug sein, wenn Sie verstehen werden, wie PRISM funktioniert.

+0

Cool, ich werde das untersuchen. –

3

Mit Abhängigkeiten sind Injektionseinheit Tests sehr einfach einzurichten, weil Sie Mocks anstelle von realen Objekten in Ihr Objekt im Test injizieren können. Sie brauchen keine Konfiguration dafür, sondern erstellen und injizieren die Mocks im Unit Test Code.

+0

Nicht nur Komponententests - Dies ist eine großartige Möglichkeit, die Test-/Entwicklungsversion der Anwendung mit Scheinkomponenten für Teile zu implementieren, die nicht getestet (oder Clients angezeigt) werden sollen. – Domchi

+0

Richtig, aber warum sollte ich Ninject dafür brauchen? –

1

Ich werde die Verwendung von DI für die Prüfung Sekunde. Ich denke momentan nur daran, DI zum Testen zu verwenden, da unsere Anwendung keine konfigurationsbasierte Flexibilität benötigt - es ist auch viel zu groß, um sie im Moment zu betrachten.

DI neigt dazu, zu saubereren, getrennteren Designs zu führen - und das bringt Vorteile rundherum.

0

Wenn Sie die Inversion der Kontrolle verwenden, helfen Sie Ihrer Klasse, so wenig wie möglich zu machen. Angenommen, Sie haben einen Windows-Dienst, der auf Dateien wartet und dann eine Reihe von Prozessen für die Datei ausführt. Einer der Prozesse ist es, es in ZIP zu konvertieren und dann per E-Mail zu versenden.

public class ZipProcessor : IFileProcessor 
{ 
    IZipService ZipService; 
    IEmailService EmailService; 

    public void Process(string fileName) 
    { 
    ZipService.Zip(fileName, Path.ChangeFileExtension(fileName, ".zip")); 
    EmailService.SendEmailTo(................); 
    } 
} 

Warum sollte diese Klasse tun müssen, um tatsächlich das Zuziehen und die E-Mailing, wenn Sie Klassen gewidmet haben könnte dies für Sie tun? Natürlich würdest du das nicht, aber das ist nur ein Hinweis auf meinen Punkt :-)

Zusätzlich zu der Implementierung der Zip und E-Mail warum sollte die Klasse wissen, welche Klasse den Dienst implementiert? Wenn Sie Schnittstellen an den Konstruktor dieses Prozessors übergeben, müssen Sie nie eine Instanz einer bestimmten Klasse erstellen, sondern erhalten alles, was sie für die Ausführung der Aufgabe benötigt.

Verwenden eines D.I.C. Sie können konfigurieren, welche Klassen bestimmte Schnittstellen implementieren und dann nur eine Instanz für Sie erstellen. Dadurch werden die Abhängigkeiten in die Klasse eingefügt.

var processor = Container.Resolve<ZipProcessor>(); 

So, jetzt haben nicht nur sauber Sie die Klasse der Funktionalität von gemeinsamen Funktionen getrennt, aber Sie haben auch die Verbraucher/Anbieter von mit einer expliziten Kenntnis voneinander verhindert. Dies macht das Lesen von Code leichter verständlich, da weniger Faktoren gleichzeitig berücksichtigt werden müssen.

Schließlich, wenn Unit-Tests können Sie verspottet Abhängigkeiten übergeben. Wenn Sie Ihren ZipProcessor testen, werden Ihre verspotteten Dienste lediglich bestätigen, dass die Klasse versucht hat, eine E-Mail zu senden, anstatt sie wirklich zu senden.

//Mock the ZIP 
var mockZipService = MockRepository.GenerateMock<IZipService>(); 
mockZipService.Expect(x => x.Zip("Hello.xml", "Hello.zip")); 

//Mock the email send 
var mockEmailService = MockRepository.GenerateMock<IEmailService>(); 
mockEmailService.Expect(x => x.SendEmailTo(.................); 

//Test the processor 
var testSubject = new ZipProcessor(mockZipService, mockEmailService); 
testSubject.Process("Hello.xml"); 

//Assert it used the services in the correct way 
mockZipService.VerifyAlLExpectations(); 
mockEmailService.VerifyAllExceptions(); 

Also kurz gesagt. Sie möchten es tun, um 01: Verhindern Sie, dass Verbraucher explizit wissen, welcher Anbieter die Dienste implementiert, die es benötigt, was bedeutet, dass weniger zu verstehen ist sofort beim Lesen von Code. 02: Erleichtern Sie das Testen von Einheiten.

Pete

+0

Es ist eine coole Antwort und alles, aber nicht genau eine Antwort auf meine Frage. Ich bin mir ziemlich bewusst, was IoC ist und was ein DI-Framework tut; Ich verstehe nur nicht den Grund, warum die Config selbst im Code auch tun. –

5

ich auf einem Weg mit krosenvold bin, hier, nur mit weniger Text: In den meisten Anwendungen, haben Sie eine genau eine Implementierung pro erforderlich „Service“. Wir schreiben einfach keine Anwendungen, bei denen jedes Objekt 10 oder mehr Implementierungen jedes Dienstes benötigt. Es wäre also sinnvoll, auf einfache Weise sagen zu können: "Dies ist die Standardimplementierung, 99% aller Objekte, die diesen Dienst nutzen, werden damit zufrieden sein".

In Tests verwenden Sie normalerweise ein spezifisches Mockup, also keine Notwendigkeit für irgendeine Konfiguration dort (da Sie die Verdrahtung manuell machen).

Das ist, worum es bei Convention-over-Configuration geht.Meistens ist die Konfiguration einfach eine Dumpwiederholung von etwas, was das DI-Framework schon wissen sollte :)

In meinen Apps benutze ich das Klassenobjekt als Schlüssel, um Implementierungen nachzuschlagen und den "Schlüssel" passiert die Standardimplementierung sein. Wenn mein DI-Framework keine Überschreibung in der Konfiguration findet, versucht es einfach, den Schlüssel zu instanziieren. Mit über 1000 "Diensten" brauche ich vier Überschreibungen. Das wäre eine Menge nutzloser XML zu schreiben.

3

erhielt ich diesen Kommentar auf meinem Blog, von Nate Kohari:

Gut, dass Sie erwägen, Ninject mit! Ninject nimmt die Haltung, dass die Konfiguration Ihres DI-Framework ist tatsächlich Teil Ihrer Anwendung, und sollte nicht öffentlich konfigurierbar sein. Wenn Sie möchten, dass bestimmte Bindungen konfigurierbar sind, können Sie einfach Ihre Ninject-Module Ihre app.config lesen lassen. Wenn Sie Ihre Bindungen in Code speichern, erhalten Sie aus der Ausführlichkeit von XML und geben Sie Typ-Sicherheit, Refactorability und Intellisense.