2009-05-23 13 views
13

Ich benutze Abhängigkeitsinjektion durch Parameter und Konstruktoren ausgiebig. Ich verstehe das Prinzip in diesem Maße und bin damit zufrieden. Bei meinen großen Projekten bekomme ich zu viele Abhängigkeiten, die injiziert werden (alles, was zweistellig ist, fühlt sich zu groß an - ich mag den Begriff "Makkaroni-Code").Kann mir jemand ausführlich erklären, wie man IOC-Behälter benutzt?

Als solche habe ich IOC-Container in Betracht gezogen. Ich habe ein paar Artikel über sie gelesen und bis jetzt habe ich den Vorteil nicht gesehen. Ich kann sehen, wie es hilft, Gruppen verwandter Objekte zu senden oder den gleichen Typ immer wieder zu bekommen. Ich bin nicht sicher, wie sie mir in meinen Projekten helfen würden, wo ich über hundert Klassen habe, die die gleiche Schnittstelle implementieren, und wo ich sie alle in unterschiedlichen Aufträgen benutze.

So kann mich jemand auf einige gute Artikel hinweisen, die nicht nur die Konzepte von IOC-Containern beschreiben (vorzugsweise ohne einen zu hüpfen), sondern auch im Detail zeigen, wie sie mir bei dieser Art von Projekt nützen und wie sie passen in den Rahmen einer großen Architektur?

Ich würde hoffen, einige nicht sprachspezifische Sachen zu sehen, aber meine bevorzugte Sprache ist, wenn nötig, C#.

Antwort

1

Ich habe keine Links, sondern können Sie mit einem Beispiel liefern:

Sie haben einen Web-Controller, der einen Dienst aufrufen muss, die eine Datenzugriffsschicht hat.

Nun, ich nehme es in Ihrem Code Sie bauen diese Objekte selbst zur Kompilierzeit. Sie verwenden ein anständiges Design-Muster, aber wenn Sie jemals die Implementierung von sagen die Dao ändern müssen, müssen Sie in Code und entfernen Sie den Code, der diese Abhängigkeit aufbaut, neu kompilieren/testen/bereitstellen. Wenn Sie jedoch einen IOC-Container verwenden, ändern Sie einfach die Klasse in der Konfiguration und starten die Anwendung neu.

3

Wenn Sie über hundert Klassen haben eine gemeinsame Schnittstelle implementieren, wird ein IoC helfen nicht viel, benötigen Sie eine Fabrik . Auf diese Weise können Sie Folgendes tun:

public interface IMyInterface{ 
    //... 
} 

public class Factory{ 

    public static IMyInterface GetObject(string param){ 
     // param is a parameter that will help the Factory decide what object to return 
     // (that is only an example, there may not be any parameter at all) 
    } 
} 

//... 
// You do not depend on a particular implementation here 
IMyInterface obj = Factory.GetObject("some param"); 

Im Inneren der Fabrik, können Sie einen IoC Container verwenden, um die Objekte abzurufen, wenn Sie mögen, aber Sie werden jede der Klassen registrieren müssen, dass die Umsetzung gegebenen Schnittstelle und ordnen Sie sie einigen Schlüsseln zu (und verwenden Sie diese Schlüssel als Parameter in GetObject() Methode).

Ein IoC ist besonders nützlich, wenn Sie Objekte abrufen müssen, die unterschiedliche Schnittstellen implementieren:

IMyInteface myObject = Container.GetObject<IMyInterface>(); 
IMyOtherInterface myOtherObject Container.GetObject<IMyOtherInterface>(); 
ISomeOtherInterface someOtherObject = Container.GetObject<ISomeOtherInterface>(); 

sehen? Nur ein Objekt, um mehrere verschiedene Typ-Objekte und keine Schlüssel zu erhalten (die Schnittstellen selbst sind die Schlüssel). Wenn Sie ein Objekt benötigen, um mehrere verschiedene Objekte zu erhalten, aber alle dieselbe Schnittstelle implementieren, wird Ihnen ein IoC nicht sehr helfen.

3

In den letzten paar Wochen habe ich den Sprung von der Abhängigkeitsinjektion zur vollständigen Inversion der Kontrolle mit Castle genommen, also verstehe ich, woher deine Frage kommt.

Einige Gründe, warum ich einen IOC-Container möchte nicht verwenden:

  1. Es ist ein kleines Projekt, das nicht so viel wachsen wird. Wenn es eine 1: 1-Beziehung zwischen Konstruktoren und Aufrufen dieser Konstruktoren gibt, wird die Verwendung eines IOC-Containers die Menge an Code, die ich schreiben muss, nicht reduzieren. Sie verletzen nicht "wiederholen Sie sich nicht", bis Sie feststellen, dass Sie das gleiche "var myObject = new MyClass (someInjectedDependency)" ein zweites Mal kopieren und einfügen.

  2. Ich muss möglicherweise bestehenden Code anpassen, um das Laden in IOC-Container zu erleichtern. Dies ist wahrscheinlich nicht notwendig, bis Sie in einige der kühleren Aspect-orientierten Programmierfunktionen kommen, aber wenn Sie vergessen haben, eine Methode virtuell zu machen, die Klasse dieser Methode versiegeln und keine Schnittstelle implementieren, und Sie Es ist unangenehm, diese Änderungen aufgrund bestehender Abhängigkeiten vorzunehmen, und dann ist der Wechsel nicht ganz so attraktiv.

  3. Es fügt meinem Projekt - und meinem Team - eine zusätzliche externe Abhängigkeit hinzu. Ich kann den Rest meines Teams davon überzeugen, dass die Strukturierung ihres Codes DI erlaubt, zu schwellen, aber ich bin derzeit der Einzige, der weiß, wie man mit Castle arbeitet. Bei kleineren, weniger komplizierten Projekten wird dies kein Problem sein. Für die größeren Projekte (die ironischerweise den größten Nutzen aus IOC-Containern ziehen würden), wenn ich mit einem IOC-Container nicht gut genug missionieren kann, wird es niemandem helfen, wenn ich mit meinem Team über den Haufen gehe.

Einige der Gründe, warum ich würde nicht wieder auf Normal DI hinwollen:

  1. ich hinzufügen oder wegnehmen Anmeldung zu einem beliebigen Anzahl meiner Klassen, ohne Zusatz von jeder Art von Trace- oder Logging-Anweisung. Die Fähigkeit zu haben, dass meine Klassen mit zusätzlicher Funktionalität verflochten werden, ohne diese Klassen zu ändern, ist extrem mächtig. Zum Beispiel:

    Logging: http://ayende.com/Blog/archive/2008/07/31/Logging--the-AOP-way.aspx

    Transaktionen: http://www.codeproject.com/KB/architecture/introducingcastle.aspx (überspringen zu dem Transaktionsabschnitt nach unten)

  2. Castle, zumindest ist so hilfreich, wenn Klassen Abhängigkeiten für Verkabelung, dass es schmerzhaft sein würde, zurück zu gehen.

Zum Beispiel eine Abhängigkeit mit Schloss fehlt:

„Can not Komponente 'MyClass' erstellen, wie es Abhängigkeiten erfüllt werden muss -Service für die folgenden Abhängigkeiten wartet.:

Dienste: - IMyService, die nicht registriert war."

fehlt eine Abhängigkeit ohne Schloss:

Objektverweis ist nicht auf eine Instanz eines Objekts

Toten Last: Die Fähigkeit injiziert Dienste zur Laufzeit zu tauschen, von Bearbeiten einer Xml - Datei Ich bin der Meinung, dass dies das am meisten betupfte Feature ist, aber ich sehe es nur als Sahnehäubchen auf dem Kuchen an.Ich würde lieber alle meine Dienstleistungen in Code, aber ich bin sicher, dass ich ein treffen werde Kopfschmerzen in der Zukunft, wo meine Meinung geändert wird.

Ich gebe zu, dass - als Neuling zu IOC und Castle - ich wahrscheinlich nur an der Oberfläche kratze, aber bis jetzt, mag ich wirklich, was ich sehe. Ich habe das Gefühl, dass die letzten Projekte, die ich damit gebaut habe, wirklich in der Lage sind, auf die unvorhersehbaren Veränderungen zu reagieren, die von Tag zu Tag in meiner Firma auftreten, ein Gefühl, das ich vorher noch nie hatte.

1

Jeremy Frey vermisst einen der größten Gründe für die Verwendung eines IOC-Containers: Es macht Ihren Code einfacher zu simulieren und zu testen.

Die Verwendung von Schnittstellen zu fördern, hat noch viele andere Vorteile: bessere Layering, einfacher, dynamisch Proxies für Dinge wie deklarative Transaktionen, aspektorientierte Programmierung und Remoting zu generieren.

Wenn Sie denken, dass IOC nur gut ist, um Anrufe zu "neuen" zu ersetzen, erhalten Sie es nicht.

+0

Ich erwähnte absichtlich nicht Tests, weil er erwähnte, dass er DI bereits benutzt. Das ist alles, was Sie wirklich brauchen, um zu verspotten und zu testen. DI ist eine Strategie; IoC-Container erleichtern diese Strategie und erleichtern die Implementierung (eine Reduzierung seines "Makkaroni-Codes"). –

+1

Dies sollte wahrscheinlich als Kommentar zu Jeremys Post gemacht worden sein ... –

13

Inversion of Control befasst sich hauptsächlich mit Abhängigkeitsverwaltung und bietet testbaren Code. Wenn von einer klassischen Herangehensweise eine Klasse abhängig ist, besteht die natürliche Tendenz darin, der Klasse, die die Abhängigkeit hat, direkte Kontrolle über die Verwaltung ihrer Abhängigkeiten zu geben. Dies bedeutet normalerweise, dass die Klasse, die die Abhängigkeit hat, ihre Abhängigkeiten innerhalb eines Konstruktors oder bei Bedarf in ihren Methoden "neu" aufbaut.

Inversion von Control ist nur das ... es invertiert, was Abhängigkeiten erzeugt, diesen Prozess externalisiert und sie in die Klasse einfügt, die die Abhängigkeit hat. Normalerweise ist die Entität, die die Abhängigkeiten erzeugt, ein sogenannter IoC-Container, der nicht nur Abhängigkeiten erzeugt und injiziert, sondern auch deren Lebensdauer verwaltet, ihren Lebensstil bestimmt (mehr dazu in einer Sekunde) und auch eine Vielzahl anbietet von anderen Fähigkeiten. (Dies basiert auf Castle MicroKernel/Windsor, das ist mein IoC-Container der Wahl ... es ist solide geschrieben, sehr funktional und erweiterbar. Es gibt andere IoC-Container, die einfacher sind, wenn Sie einfachere Anforderungen haben, wie Ninject, Microsoft Unity und Spring.NET.)

Bedenken Sie, dass Sie eine interne Anwendung haben, die entweder in einem lokalen Kontext oder einem Remote-Kontext verwendet werden kann. Abhängig von einigen erkennbaren Faktoren muss Ihre Anwendung möglicherweise "lokale" Implementierungen Ihrer Dienste laden, und in anderen Fällen muss sie möglicherweise "entfernte" Implementierungen Ihrer Dienste laden. Wenn Sie dem klassischen Ansatz folgen und Ihre Abhängigkeiten direkt in der Klasse erstellen, die diese Abhängigkeiten hat, dann wird diese Klasse gezwungen sein, zwei sehr wichtige Regeln über die Softwareentwicklung zu brechen: Trennung von Bedenken und Einzelverantwortlichkeit. Sie überschreiten die Grenzen der Besorgnis, weil Ihre Klasse sich nun sowohl mit ihrem eigentlichen Zweck beschäftigt, als auch mit der Frage, welche Abhängigkeiten sie erstellen soll und wie. Die Klasse ist jetzt auch für viele Dinge verantwortlich, und nicht nur für eine Sache, und hat viele Gründe, sich zu ändern: ihr intrinsischer Zweck ändert sich, der Erzeugungsprozess für ihre Abhängigkeiten ändert sich, die Art, wie sich entfernte Abhängigkeiten ändern, welche Abhängigkeiten ihre Abhängigkeiten benötigen , etc.

Indem Sie Ihr Abhängigkeitsmanagement umkehren, können Sie Ihre Systemarchitektur verbessern und SoC und SR verwalten (oder möglicherweise erreichen, wenn Sie zuvor aufgrund von Abhängigkeiten nicht in der Lage waren.) Seit einer externen Entität, dem IoC-Container, wird nun gesteuert Wie Ihre Abhängigkeiten erstellt und injiziert werden, können Sie auch zusätzliche Fähigkeiten erhalten. Der Container kann die Lebenszyklen Ihrer Abhängigkeiten verwalten und sie flexibler erstellen und zerstören, um die Effizienz zu verbessern. Sie erhalten auch die Fähigkeit, die Lebensstile Ihrer Objekte zu verwalten. Wenn Sie eine Art von Abhängigkeit haben, die sehr häufig erstellt, verwendet und zurückgegeben wird, aber wenig oder gar keinen Status hat (z. B. Fabriken), können Sie ihnen einen gepoolten Lebensstil geben, der dem Container automatisch mitteilt ein Objektpool für diesen bestimmten Abhängigkeitstyp. Viele Lebensstile existieren, und ein Behälter wie Schloss Windsor gibt Ihnen normalerweise die Fähigkeit, Ihr eigenes zu verursachen.

Die besseren IoC-Container, wie Castle Windsor, bieten auch eine große Erweiterbarkeit. Standardmäßig können Sie in Windsor Instanzen lokaler Typen erstellen. Es ist möglich, Facilities zu erstellen, die Windsors Erstellungsfunktionen erweitern, um Web-Service-Proxies und WCF-Service-Hosts dynamisch zur Laufzeit zu erstellen, sodass sie nicht manuell oder statisch mit Tools wie svcutil erstellt werden müssen .) Viele Möglichkeiten existieren, um IoC-Unterstützung für existierende Frameworks, wie NHibernate, ActiveRecord etc. zu bringen.

Schließlich erzwingt IoC einen Stil der Kodierung, der Einheit-testbaren Code sicherstellt. Einer der Schlüsselfaktoren, um Code-Einheiten testbar zu machen, ist die Externalisierung des Abhängigkeitsmanagements. Ohne die Möglichkeit, alternative (verspottete, stumpfe usw.) Abhängigkeiten bereitzustellen, ist das Testen einer einzelnen "Einheit" von Code in Isolation eine sehr schwierige Aufgabe, so dass Integrationstests der einzige alternative Stil automatisierter Tests sind. Da IoC erfordert, dass Ihre Klassen Abhängigkeiten durch Injection (durch Konstruktor, Eigenschaft oder Methode) akzeptieren, wird jede Klasse normalerweise, wenn nicht immer, auf eine einzige Verantwortung von ordnungsgemäß separierten Interessen und vollständig ansprechbaren Abhängigkeiten reduziert.

IoC = bessere Architektur, größere Kohäsion, bessere Trennung von Belangen, Klassen, die leichter auf eine einzige Verantwortung reduziert werden können, leicht konfigurierbare und austauschbare Abhängigkeiten (oft ohne eine Neukompilierung Ihres Codes), flexible Abhängigkeits-Stile und Leben Zeitmanagement und testbarer Code für Einheiten. IoC ist so etwas wie ein Lebensstil ... eine Philosophie, ein Ansatz, um gemeinsame Probleme zu lösen und kritische Best Practices wie SoC und SR zu treffen.

Sogar (oder besser gesagt, besonders) mit Hunderten von verschiedenen Implementierungen einer einzelnen Schnittstelle hat IoC viel zu bieten. Es kann eine Weile dauern, bis sich der Kopf vollständig darum dreht, aber sobald Sie vollständig verstanden haben, was IoC ist und was es für Sie tun kann, werden Sie niemals etwas anders machen wollen (außer vielleicht Embedded Systementwicklung ...)

+2

Sehr nette Antwort. – Klinger

+0

Vielen Dank für diese Erklärung !!! – John

0

IoC-Container tun normalerweise die Dependency-Injektionen, die in einigen Projekten keine große Sache sind, aber einige der Frameworks, die IoC-Container bieten, bieten andere Dienste, die es wert sind, sie zu verwenden. Schloss zum Beispiel hat eine vollständige Liste von Diensten neben einem IoC Container. Dynamische Proxies, Transaktionsverwaltung und NHibernate Einrichtungen sind nur einige von ihnen. Dann sollten Sie IoC Contianer als Teil eines Anwendungs-Frameworks betrachten.

hier, warum ich ein IoC-Container verwenden: 1.Writing Unit-Tests einfacher .Actually werden Sie verschiedene Konfigurationen schreiben zu tun verschiedene Dinge 2.Adding verschiedene Plugins für verschiedene Szenarien (für verschiedene Kunden zum Beispiel) 3. Abfangen von Klassen, um unserem Code verschiedene Aspekte hinzuzufügen. 4. Da wir NHibernate verwenden, sind Transaktionsverwaltung und NHibernate-Einrichtungen von Castle sehr hilfreich bei der Entwicklung und Pflege unseres Codes. Es ist so, als ob alle technischen Aspekte unserer Anwendung mit einem Anwendungs-Framework behandelt werden und wir Zeit haben, darüber nachzudenken, was Kunden wirklich wollen.

Verwandte Themen