Was sind die Vorteile von Abhängigkeitsinjektionsbehältern?


104

Ich verstehe die Vorteile der Abhängigkeitsinjektion selbst. Nehmen wir zum Beispiel den Frühling. Ich verstehe auch die Vorteile anderer Spring-Funktionen wie AOP, Helfer verschiedener Art usw. Ich frage mich nur, welche Vorteile die XML-Konfiguration bietet, z.

<bean id="Mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
  <property name="girlfriend" ref="Mary"/>
</bean>

im Vergleich zu einfachem alten Java-Code wie:

Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);

Das ist einfacher zu debuggen, die Kompilierungszeit überprüft und kann von jedem verstanden werden, der nur Java kennt. Was ist der Hauptzweck eines Abhängigkeitsinjektions-Frameworks? (oder ein Stück Code, der seine Vorteile zeigt.)


UPDATE:
Im Falle von

IService myService;// ...
public void doSomething() {  
  myService.fetchData();
}

Wie kann das IoC-Framework erraten, welche Implementierung von myService injiziert werden soll, wenn es mehr als eine gibt? Wenn es nur eine Implementierung einer bestimmten Schnittstelle gibt und ich den IoC-Container automatisch für die Verwendung entscheiden lasse, wird sie unterbrochen, nachdem eine zweite Implementierung angezeigt wird. Und wenn es absichtlich nur eine mögliche Implementierung einer Schnittstelle gibt, müssen Sie sie nicht injizieren.

Es wäre wirklich interessant, eine kleine Konfiguration für IoC zu sehen, die die Vorteile zeigt. Ich benutze Spring schon eine Weile und kann ein solches Beispiel nicht liefern. Und ich kann einzelne Zeilen anzeigen, die die Vorteile von Ruhezustand, DWR und anderen von mir verwendeten Frameworks demonstrieren.


UPDATE 2:
Mir ist klar, dass die IoC-Konfiguration ohne erneutes Kompilieren geändert werden kann. Ist es wirklich so eine gute Idee? Ich kann verstehen, wenn jemand DB-Anmeldeinformationen ändern möchte, ohne sie neu zu kompilieren - er ist möglicherweise kein Entwickler. Wie oft ändert in Ihrer Praxis eine andere Person als der Entwickler die IoC-Konfiguration? Ich denke, dass es für Entwickler keine Anstrengung gibt, diese bestimmte Klasse neu zu kompilieren, anstatt die Konfiguration zu ändern. Und für Nicht-Entwickler möchten Sie wahrscheinlich sein Leben einfacher machen und eine einfachere Konfigurationsdatei bereitstellen.


UPDATE 3:

Externe Konfiguration der Zuordnung zwischen Schnittstellen und ihren konkreten Implementierungen

Was ist so gut daran, es zu erweitern? Sie machen nicht Ihren gesamten Code extern, während Sie dies definitiv können - platzieren Sie ihn einfach in der Datei ClassName.java.txt, lesen und kompilieren Sie manuell im laufenden Betrieb - wow, Sie haben eine Neukompilierung vermieden. Warum sollte das Kompilieren vermieden werden?!

Sie sparen Codierungszeit, da Sie Zuordnungen deklarativ und nicht in einem Prozedurcode bereitstellen

Ich verstehe, dass manchmal deklarativer Ansatz Zeit spart. Zum Beispiel deklariere ich nur einmal, dass eine Zuordnung zwischen einer Bean-Eigenschaft und einer DB-Spalte und dem Ruhezustand diese Zuordnung beim Laden, Speichern, Erstellen von SQL basierend auf HSQL usw. verwendet. Hier funktioniert der deklarative Ansatz. Im Fall von Spring (in meinem Beispiel) hatte die Deklaration mehr Zeilen und die gleiche Ausdruckskraft wie der entsprechende Code. Wenn es ein Beispiel gibt, bei dem eine solche Deklaration kürzer als Code ist, würde ich es gerne sehen.

Das Inversion of Control-Prinzip ermöglicht ein einfaches Testen von Einheiten, da Sie echte Implementierungen durch gefälschte ersetzen können (z. B. das Ersetzen der SQL-Datenbank durch eine In-Memory-Datenbank).

Ich verstehe die Inversion der Kontrollvorteile (ich ziehe es vor, das hier diskutierte Entwurfsmuster als Abhängigkeitsinjektion zu bezeichnen, da IoC allgemeiner ist - es gibt viele Arten der Kontrolle, und wir invertieren nur eine davon - die Kontrolle der Initialisierung). Ich habe gefragt, warum jemand jemals etwas anderes als eine Programmiersprache dafür braucht. Ich kann echte Implementierungen definitiv durch gefälschte implementieren, indem ich Code verwende. Und dieser Code drückt dasselbe aus wie die Konfiguration - er initialisiert nur Felder mit gefälschten Werten.

mary = new FakeFemale();

Ich verstehe die Vorteile von DI. Ich verstehe nicht, welche Vorteile die externe XML-Konfiguration im Vergleich zur Konfiguration von Code bietet, der dasselbe tut. Ich denke nicht, dass das Kompilieren vermieden werden sollte - ich kompiliere jeden Tag und bin noch am Leben. Ich denke, die Konfiguration von DI ist ein schlechtes Beispiel für einen deklarativen Ansatz. Die Deklaration kann nützlich sein, wenn sie einmal deklariert wird UND mehrmals auf unterschiedliche Weise verwendet wird - wie z. B. im Ruhezustand cfg, wo die Zuordnung zwischen Bean-Eigenschaft und DB-Spalte zum Speichern, Laden, Erstellen von Suchabfragen usw. verwendet wird. Die Spring DI-Konfiguration kann problemlos übersetzt werden Konfigurieren von Code, wie am Anfang dieser Frage, kann es nicht? Und es wird nur für die Bean-Initialisierung verwendet, nicht wahr? Was bedeutet, dass ein deklarativer Ansatz hier nichts hinzufügt, oder?

Wenn ich die Zuordnung für den Ruhezustand deklariere, gebe ich nur einige Informationen für den Ruhezustand an, und es funktioniert basierend darauf - ich sage ihm nicht, was zu tun ist. Im Falle des Frühlings sagt meine Erklärung dem Frühling genau, was zu tun ist - warum also deklarieren, warum nicht einfach?


LETZTES UPDATE:
Leute, viele Antworten erzählen mir von der Abhängigkeitsinjektion, von der ich weiß, dass sie gut ist. Die Frage bezieht sich auf den Zweck der DI-Konfiguration anstelle der Initialisierung von Code. Ich denke eher, dass die Initialisierung von Code kürzer und klarer ist. Die einzige Antwort, die ich bisher auf meine Frage erhalten habe, ist, dass ein erneutes Kompilieren vermieden wird, wenn sich die Konfiguration ändert. Ich denke, ich sollte eine andere Frage stellen, da es für mich ein großes Geheimnis ist, warum das Kompilieren in diesem Fall vermieden werden sollte.


21
Endlich hatte jemand den Mut, diese Frage zu stellen. Warum sollten Sie in der Tat eine Neukompilierung vermeiden, wenn sich Ihre Implementierung auf Kosten der Beeinträchtigung (oder zumindest Verschlechterung) der Tool- / IDE-Unterstützung ändert?
Christian Klauser

3
Es scheint, dass der Titel nicht ganz richtig ist. Der Autor hat gesagt, dass IOC-Container in Ordnung sind, scheint jedoch Probleme mit der Verwendung der XML-Konfiguration anstelle der Konfiguration über Code zu haben (und auch fair genug). Ich würde vielleicht vorschlagen: "Was sind die Vorteile der Konfiguration von IOC-Containern über XML oder andere Nicht-Code-Ansätze?"
Orion Edwards

@ Orion-Beispiele, die ich bereitgestellt habe (mit männlich und weiblich), erfordern keinen IOC-Container. Mir geht es gut mit IOC; Die Verwendung von Containern, ob sie mit XML konfiguriert sind oder nicht, ist für mich immer noch eine offene Frage.
Pavel Feldman

@Orion 2: Während ich in den meisten Projekten eine Form von IOC verwende, profitieren einige von ihnen vom IOC-Container ebenso wie vom Container für variable Zuweisungen oder vom If-Anweisungscontainer - nur die Sprache reicht mir oft aus. Ich habe kein Problem damit, Projekte, an denen ich arbeite, neu zu kompilieren und den Code für die Initialisierung von dev / test / Production bequem zu trennen. Für mich ist der Titel also in Ordnung.
Pavel Feldman

Ich sehe Probleme mit der Probe. Eines
Jacek Cz

Antworten:


40

Für mich liegt einer der Hauptgründe für die Verwendung eines IoC (und die Verwendung einer externen Konfiguration) in den beiden Bereichen:

  • Testen
  • Produktionswartung

Testen

Wenn Sie Ihre Tests in drei Szenarien aufteilen (was bei der Entwicklung in großem Maßstab ziemlich normal ist):

  1. Unit Testing
  2. Integrationstests
  3. Black-Box-Test

Was Sie tun möchten, ist für die letzten beiden Testszenarien (Integration & Black Box), keinen Teil der Anwendung neu zu kompilieren.

Wenn Sie in einem Ihrer Testszenarien die Konfiguration ändern müssen (z. B. eine andere Komponente verwenden, um eine Bankintegration nachzuahmen, oder eine Leistungslast ausführen), kann dies problemlos gehandhabt werden (dies unterliegt den Vorteilen der Konfiguration der DI-Seite eines IoC obwohl.

Wenn Ihre App entweder an mehreren Standorten (mit unterschiedlicher Server- und Komponentenkonfiguration) verwendet wird oder eine sich ändernde Konfiguration in der Live-Umgebung aufweist, können Sie in den späteren Testphasen überprüfen, ob die App diese Änderungen verarbeitet.

Produktion

Als Entwickler haben Sie (und sollten) keine Kontrolle über die Produktionsumgebung (insbesondere wenn Ihre App an mehrere Kunden oder separate Sites verteilt wird). Für mich ist dies der eigentliche Vorteil der Verwendung einer IoC- und einer externen Konfiguration , da es an der Infrastruktur- / Produktionsunterstützung liegt, die Live-Umgebung zu optimieren und anzupassen, ohne zu Entwicklern und durch Tests zurückkehren zu müssen (höhere Kosten, wenn sie nur eine Komponente verschieben möchten).

Zusammenfassung

Die Hauptvorteile der externen Konfiguration eines IoC bestehen darin, anderen (Nicht-Entwicklern) die Möglichkeit zu geben, Ihre Anwendung zu konfigurieren. Nach meiner Erfahrung ist dies nur unter bestimmten Umständen nützlich:

  • Die Anwendung wird an mehrere Standorte / Clients verteilt, an denen sich die Umgebungen unterscheiden.
  • Begrenzte Entwicklungskontrolle / Eingabe über die Produktionsumgebung und das Setup.
  • Szenarien testen.

In der Praxis habe ich festgestellt, dass es im Laufe der Zeit besser ist, jemand anderem die Möglichkeit zu geben, die Konfiguration zu ändern, selbst wenn Sie etwas entwickeln, auf das Sie die Kontrolle über die Umgebung haben.

  • Bei der Entwicklung wissen Sie nicht, wann es sich ändern wird (die App ist so nützlich, dass Ihr Unternehmen sie an eine andere Person verkauft).
  • Ich möchte nicht jedes Mal den Code ändern müssen, wenn eine geringfügige Änderung angefordert wird, die durch das Einrichten und Verwenden eines guten Konfigurationsmodells hätte behoben werden können.

Hinweis: Die Anwendung bezieht sich auf die vollständige Lösung (nicht nur auf die ausführbare Datei), also auf alle Dateien, die für die Ausführung der Anwendung erforderlich sind .


14

Die Abhängigkeitsinjektion ist ein Codierungsstil, der seine Wurzeln in der Beobachtung hat, dass die Objektdelegierung normalerweise ein nützlicheres Entwurfsmuster als die Objektvererbung ist (dh das Objekt hat eine Beziehung, die nützlicher ist als das Objekt eine Beziehung). Eine weitere Zutat ist jedoch erforderlich, damit DI funktioniert, nämlich das Erstellen von Objektschnittstellen. Durch die Kombination dieser beiden leistungsstarken Entwurfsmuster erkannten Softwareentwickler schnell, dass sie flexiblen, lose gekoppelten Code erstellen konnten, und so entstand das Konzept der Abhängigkeitsinjektion. Erst als die Objektreflexion in bestimmten Hochsprachen verfügbar wurde, nahm DI wirklich Fahrt auf. Die Reflexionskomponente ist der Kern der meisten von heute. '

Eine Sprache muss sowohl normale objektorientierte Programmiertechniken als auch Objektschnittstellen und Objektreflexion (z. B. Java und C #) gut unterstützen. Während Sie Programme mit DI-Mustern in C ++ - Systemen erstellen können, verhindert die fehlende Reflexionsunterstützung in der eigentlichen Sprache, dass Anwendungsserver und andere DI-Plattformen unterstützt werden, und schränkt daher die Ausdruckskraft der DI-Muster ein.

Stärken eines Systems, das mit DI-Mustern aufgebaut ist:

  1. DI-Code ist viel einfacher wiederzuverwenden, da die "abhängige" Funktionalität in genau definierte Schnittstellen extrapoliert wird, sodass separate Objekte, deren Konfiguration von einer geeigneten Anwendungsplattform verwaltet wird, nach Belieben in andere Objekte eingefügt werden können.
  2. DI-Code ist viel einfacher zu testen. Die vom Objekt ausgedrückte Funktionalität kann in einer Black Box getestet werden, indem Scheinobjekte erstellt werden, die die von Ihrer Anwendungslogik erwarteten Schnittstellen implementieren.
  3. DI-Code ist flexibler. Es ist von Natur aus lose gekoppelter Code - bis zum Äußersten. Auf diese Weise kann der Programmierer auswählen, wie Objekte ausschließlich anhand ihrer erforderlichen Schnittstellen an einem Ende und ihrer ausgedrückten Schnittstellen am anderen Ende verbunden werden.
  4. Durch die externe (XML) Konfiguration von DI-Objekten können andere Ihren Code in unvorhergesehene Richtungen anpassen.
  5. Die externe Konfiguration ist auch insofern eine Trennung des Anliegenmusters, als alle Probleme der Objektinitialisierung und des Objekt-Interdependenz-Managements vom Anwendungsserver behandelt werden können.
  6. Beachten Sie, dass für die Verwendung des DI-Musters keine externe Konfiguration erforderlich ist. Für einfache Verbindungen ist häufig ein kleines Builder-Objekt ausreichend. Es gibt einen Kompromiss in der Flexibilität zwischen den beiden. Ein Builder-Objekt ist keine so flexible Option wie eine extern sichtbare Konfigurationsdatei. Der Entwickler des DI-Systems muss die Vorteile der Flexibilität gegenüber der Bequemlichkeit abwägen und darauf achten, dass eine kleine Feinkornkontrolle über die Objektkonstruktion, wie in einer Konfigurationsdatei angegeben, die Verwirrung und die Wartungskosten auf der ganzen Linie erhöhen kann.

Auf jeden Fall scheint DI-Code umständlicher zu sein. Die Nachteile all dieser XML-Dateien, die Objekte konfigurieren, die in andere Objekte eingefügt werden sollen, erscheinen schwierig. Dies ist jedoch der Punkt von DI-Systemen. Durch Ihre Fähigkeit, Codeobjekte als eine Reihe von Konfigurationseinstellungen zu mischen und abzugleichen, können Sie komplexe Systeme mit Code von Drittanbietern mit minimaler Codierung erstellen.

Das in der Frage bereitgestellte Beispiel berührt lediglich die Oberfläche der Ausdruckskraft, die eine ordnungsgemäß faktorisierte DI-Objektbibliothek bereitstellen kann. Mit etwas Übung und viel Selbstdisziplin stellen die meisten DI-Praktiker fest, dass sie Systeme erstellen können, die eine 100% ige Testabdeckung des Anwendungscodes aufweisen. Dieser eine Punkt allein ist außergewöhnlich. Dies ist keine 100% ige Testabdeckung einer kleinen Anwendung mit einigen hundert Codezeilen, sondern eine 100% ige Testabdeckung von Anwendungen mit hunderttausenden Codezeilen. Ich bin nicht in der Lage, andere Entwurfsmuster zu beschreiben, die dieses Maß an Testbarkeit bieten.

Sie haben insofern Recht, als eine Anwendung von nur 10 Codezeilen leichter zu verstehen ist als mehrere Objekte plus eine Reihe von XML-Konfigurationsdateien. Wie bei den leistungsstärksten Entwurfsmustern werden die Vorteile jedoch festgestellt, wenn Sie dem System weiterhin neue Funktionen hinzufügen.

Kurz gesagt, DI-basierte Anwendungen in großem Maßstab sind sowohl einfacher zu debuggen als auch leichter zu verstehen. Während die XML-Konfiguration nicht auf Kompilierungszeit überprüft ist, erhalten alle Anwendungsdienste, die diesem Autor bekannt sind, Fehlermeldungen, wenn sie versuchen, ein Objekt mit einer inkompatiblen Schnittstelle in ein anderes Objekt einzufügen. Die meisten bieten eine Überprüfungsfunktion, die alle bekannten Objektkonfigurationen abdeckt. Dies erfolgt einfach und schnell, indem überprüft wird, ob das zu injizierende Objekt A die von Objekt B für alle konfigurierten Objektinjektionen erforderliche Schnittstelle implementiert.


4
Vorteile von DI verstehen. Ich verstehe nicht, welche Vorteile die externe XML-Konfiguration im Vergleich zur Konfiguration von Code bietet, der dasselbe tut. Die von Ihnen genannten Vorteile ergeben sich aus dem DI-Entwurfsmuster. Die Frage betraf die Vorteile der DI-Konfiguration im Vergleich zum einfachen Initialisierungscode.
Pavel Feldman

> Externe Konfiguration ist auch eine Trennung ... Die Trennung der Konfiguration ist das Herzstück von DI, was gut ist. Und es kann mit Initialisierungscode de dome. Was fügt cfg im Vergleich zum Initialisieren von Code hinzu? Für mich scheint es, dass jede Zeile von cfg eine entsprechende Initialisierungscodezeile hat.
Pavel Feldman

7

Dies ist eine etwas geladene Frage, aber ich stimme eher zu, dass große Mengen an XML-Konfiguration nicht wirklich viel Nutzen bringen. Ich mag es, wenn meine Anwendungen so leicht wie möglich von Abhängigkeiten sind, einschließlich der umfangreichen Frameworks.

Sie vereinfachen den Code häufig, haben aber auch einen Komplexitätsaufwand, der das Aufspüren von Problemen ziemlich schwierig macht (ich habe solche Probleme aus erster Hand gesehen, und mit reinem Java würde ich mich viel wohler fühlen).

Ich denke, es hängt ein bisschen vom Stil ab und davon, womit Sie vertraut sind. Fliegen Sie gerne Ihre eigene Lösung und haben Sie den Vorteil, sie genau zu kennen, oder setzen Sie auf vorhandene Lösungen, die sich als schwierig erweisen können, wenn die Konfiguration nicht stimmt. t genau richtig? Es ist alles ein Kompromiss.

Die XML-Konfiguration ist jedoch ein kleiner Ärger von mir ... Ich versuche, sie um jeden Preis zu vermeiden.


5

Jedes Mal, wenn Sie Ihren Code in Daten ändern können, machen Sie einen Schritt in die richtige Richtung.

Das Codieren von Daten bedeutet, dass Ihr Code selbst allgemeiner und wiederverwendbarer ist. Dies bedeutet auch, dass Ihre Daten möglicherweise in einer Sprache angegeben werden, die genau zu ihnen passt.

Eine XML-Datei kann auch in eine GUI oder ein anderes Tool eingelesen und leicht pragmatisch bearbeitet werden. Wie würden Sie das mit dem Codebeispiel machen?

Ich berücksichtige ständig Dinge, die die meisten Leute als Code in Daten implementieren würden. Dadurch wird der verbleibende Code VIEL sauberer. Ich finde es unvorstellbar, dass Leute ein Menü im Code und nicht als Daten erstellen - es sollte offensichtlich sein, dass es aufgrund des Boilerplates einfach falsch ist, es im Code zu tun.


macht Sinn, habe in dieser Perspektive nicht darüber
Pavel Feldman


@Casebash Das ist eine interessante Sichtweise - ich würde mich erstaunlich für ein Beispiel interessieren. Ich finde, dass alles, was ich in Daten verschieben kann, hilft. Ich finde auch, dass, wenn ich genau das tue, was Sie sagen, die Sprache tatsächlich verbessert wird, weil es sich um DSL handelt - aber selbst dann ist eine ernsthafte Rechtfertigung erforderlich, um eine völlig neue Sprache zu erstellen.
Bill K

1
"Jedes Mal, wenn Sie Ihren Code in Daten ändern können, machen Sie einen Schritt in die richtige Richtung." Willkommen beim Anti-Pattern für Soft Coding.
Raedwald

@Raedwald Sie sprechen von Externalisierung, was sehr schwierig sein kann, wenn Sie nicht wissen, was Sie tun (und der Grund, warum jemand inkompetent es versucht hat, fehlgeschlagen ist und es als Anti-Muster bezeichnet hat). Positivere Beispiele wären Injektion, Iteratoren , fast alles mit Anmerkungen, alles, was mit Arrays initialisiert wird. Die meisten großartigen Programmierstrukturen sind ein Versuch, die Unterschiede in Ihrem Code herauszufiltern und die verbleibenden Elemente zu kombinieren, um sie mit etwas zu steuern, das besser gruppiert und verwaltet werden kann.
Bill K

3

Der Grund für die Verwendung eines DI-Containers besteht darin, dass in Ihrem Code nicht eine Milliarde Eigenschaften vorkonfiguriert sein müssen, die lediglich Getter und Setter sind. Möchten Sie wirklich alle mit neuem X () fest codieren? Natürlich können Sie eine Standardeinstellung festlegen, aber der DI-Container ermöglicht die Erstellung von Singletons, was äußerst einfach ist und es Ihnen ermöglicht, sich auf die Details des Codes zu konzentrieren, nicht auf die sonstige Aufgabe, ihn zu initialisieren.

Mit Spring können Sie beispielsweise die InitializingBean-Schnittstelle implementieren und eine afterPropertiesSet-Methode hinzufügen (Sie können auch eine "init-Methode" angeben, um zu vermeiden, dass Ihr Code an Spring gekoppelt wird). Mit diesen Methoden können Sie sicherstellen, dass alle in Ihrer Klasseninstanz als Feld angegebenen Schnittstellen beim Start korrekt konfiguriert sind. Anschließend müssen Sie Ihre Getter und Setter nicht mehr auf Null prüfen (vorausgesetzt, Sie lassen zu, dass Ihre Singletons threadsicher bleiben ).

Darüber hinaus ist es viel einfacher, komplexe Initialisierungen mit einem DI-Container durchzuführen, als sie selbst durchzuführen. Zum Beispiel unterstütze ich bei der Verwendung von XFire (nicht CeltiXFire, wir verwenden nur Java 1.4). Die App verwendete Spring, aber leider den Konfigurationsmechanismus services.xml von XFire. Wenn eine Sammlung von Elementen deklarieren musste, dass sie NULL oder mehr Instanzen anstelle von EINER oder mehreren Instanzen hatte, musste ich einen Teil des bereitgestellten XFire-Codes für diesen bestimmten Dienst überschreiben.

In seinem Spring Beans-Schema sind bestimmte XFire-Standardeinstellungen definiert. Wenn wir also Spring zum Konfigurieren der Dienste verwendet hätten, könnten die Beans verwendet worden sein. Stattdessen musste ich eine Instanz einer bestimmten Klasse in der Datei services.xml angeben, anstatt die Beans zu verwenden. Dazu musste ich den Konstruktor bereitstellen und die in der XFire-Konfiguration deklarierten Referenzen einrichten. Die wirkliche Änderung, die ich vornehmen musste, erforderte, dass ich eine einzelne Klasse überlastete.

Dank der Datei services.xml musste ich jedoch vier neue Klassen erstellen und ihre Standardeinstellungen entsprechend den Standardeinstellungen in den Spring-Konfigurationsdateien in ihren Konstruktoren festlegen. Wenn wir die Spring-Konfiguration hätten verwenden können, hätte ich einfach sagen können:

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

Stattdessen sah es eher so aus:

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

Das Nettoergebnis ist also, dass vier zusätzliche, meist sinnlose Java-Klassen zur Codebasis hinzugefügt werden mussten, um den Effekt zu erzielen, den eine zusätzliche Klasse und einige einfache Abhängigkeitscontainerinformationen erzielten. Dies ist nicht die "Ausnahme, die die Regel beweist", sondern die Regel ... Der Umgang mit Macken im Code ist viel sauberer, wenn die Eigenschaften bereits in einem DI-Container bereitgestellt werden und Sie sie einfach an eine spezielle Situation anpassen. was meistens passiert.


3

Ich habe deine Antwort

Es gibt offensichtlich Kompromisse bei jedem Ansatz, aber externalisierte XML-Konfigurationsdateien sind nützlich für die Unternehmensentwicklung, bei der Build-Systeme zum Kompilieren des Codes und nicht Ihrer IDE verwendet werden. Wenn Sie das Build-System verwenden, möchten Sie möglicherweise bestimmte Werte in Ihren Code einfügen - beispielsweise die Version des Builds (was schmerzhaft sein kann, wenn Sie jedes Mal, wenn Sie kompilieren, manuell aktualisieren müssen). Der Schmerz ist größer, wenn Ihr Build-System Code von einem Versionskontrollsystem abruft. Wenn Sie einfache Werte zur Kompilierungszeit ändern, müssen Sie eine Datei ändern, festschreiben, kompilieren und dann jedes Mal für jede Änderung zurücksetzen. Dies sind keine Änderungen, die Sie in Ihre Versionskontrolle übernehmen möchten.

Andere nützliche Anwendungsfälle in Bezug auf das Build-System und externe Konfigurationen:

  • Einfügen von Styles / Stylesheets für eine einzelne Codebasis für verschiedene Builds
  • Einfügen verschiedener Sätze dynamischer Inhalte (oder Verweise darauf) für Ihre einzelne Codebasis
  • Einfügen eines Lokalisierungskontexts für verschiedene Builds / Clients
  • Ändern eines Webservice-URI in einen Sicherungsserver (wenn der Hauptserver ausfällt)

Update: Alle obigen Beispiele betrafen Dinge, die nicht unbedingt Abhängigkeiten von Klassen erforderten. Sie können jedoch problemlos Fälle erstellen, in denen sowohl ein komplexes Objekt als auch eine Automatisierung erforderlich sind - zum Beispiel:

  • Stellen Sie sich vor, Sie hätten ein System, mit dem der Verkehr Ihrer Website überwacht wird. Abhängig von der Anzahl der gleichzeitigen Benutzer wird ein Protokollierungsmechanismus aktiviert / deaktiviert. Möglicherweise wird bei ausgeschaltetem Mechanismus ein Stummelobjekt an seine Stelle gesetzt.
  • Stellen Sie sich vor, Sie hätten ein Webkonferenzsystem, in dem Sie je nach Anzahl der Benutzer die Möglichkeit ausschalten möchten, P2P je nach Anzahl der Teilnehmer durchzuführen

+1 für die Hervorhebung des Unternehmensaspekts ganz oben. Das Testen von schlecht geschriebenem Legacy-Code kann manchmal ein tagelanger Albtraum sein.
Richard Le Mesurier

2

Sie müssen Ihren Code nicht jedes Mal neu kompilieren, wenn Sie Änderungen an der Konfiguration vornehmen. Dies vereinfacht die Programmbereitstellung und -wartung. Zum Beispiel können Sie eine Komponente mit nur einer Änderung in der Konfigurationsdatei gegen eine andere austauschen.


Einsatz? wahrscheinlich ... Wartung der Bereitstellung? wahrscheinlich ... Wartung des Codes? Ich neige dazu, nein zu denken ... Das Debuggen durch Frameworks bereitet große Kopfschmerzen, und Pojos sind in dieser Hinsicht viel einfacher zu handhaben.
Mike Stone

1
Mike, ich habe nichts über Code gesagt. Wir alle wissen, dass die XML-Konfiguration scheiße ist :)
aku

Hmm .. wie oft wechseln Sie Komponenten ohne Neukompilierung und wofür? Ich verstehe, wenn jemand DB-Anmeldeinformationen ändert und das Programm nicht neu kompilieren möchte - er ist möglicherweise nicht derjenige, der es entwickelt. Aber ich kann mir kaum vorstellen, dass jemand anderes als der Entwickler die Federkonfiguration ändert
Pavel Feldman,

In der Regel tritt diese Situation auf, wenn Sie ein Programm für Hunderte von Clients bereitstellen müssen. In solchen Situationen ist es viel einfacher, die Konfiguration zu ändern, als eine neue Version des Produkts bereitzustellen. Sie haben Recht, über Entwickler zu sagen. Normalerweise erstellt der Entwickler eine neue CFG und der Administrator stellt sie bereit.
Aku

2

Sie können eine neue Implementierung für Freundin einfügen. So kann eine neue Frau injiziert werden, ohne Ihren Code neu zu kompilieren.

<bean id="jane" class="foo.bar.HotFemale">
  <property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
  <property name="girlfriend" ref="jane"/>
</bean>

(Oben wird davon ausgegangen, dass Female und HotFemale dieselbe GirlfFriend-Schnittstelle implementieren.)


Warum sind logische Änderungen ohne Neukompilierung eine gute Idee?
Pavel Feldman

Ich kann definitiv HotFemale jane = new HotFmale (); jane.setAge (19); john.setGirlfriend (Jane); Der einzige Unterschied ist also, dass cfg ohne erneutes Kompilieren geändert werden kann? Es scheint eine häufige Antwort zu sein, wenn der Frühling besprochen wird. Warum?! Warum ist es gut, das Kompilieren zu vermeiden?
Pavel Feldman

Nun, ich kann den Code besser testen, ich kann das weibliche Objekt verspotten.
Paul Whelan

@Pavel Feldman: Wenn Sie die App bereits auf dem Client bereitgestellt haben, ist dies einfacher.
Andrei Rînea

1

In der .NET-Welt bieten die meisten IoC-Frameworks sowohl XML- als auch Code-Konfiguration.

StructureMap und Ninject verwenden beispielsweise fließende Schnittstellen, um Container zu konfigurieren. Sie müssen keine XML-Konfigurationsdateien mehr verwenden. Spring, das auch in .NET vorhanden ist, stützt sich stark auf XML-Dateien, da es seine historische Hauptkonfigurationsoberfläche ist. Es ist jedoch weiterhin möglich, Container programmgesteuert zu konfigurieren.


Das ist großartig, dass ich endlich Code für das verwenden kann, was er verwenden soll :) Aber warum brauche ich überhaupt etwas anderes als Programmiersprache, um solche Dinge zu tun?
Pavel Feldman

Ich denke, das liegt daran, dass XML Laufzeitänderungen oder zumindest Konfigurationsänderungen zulässt, ohne das Projekt neu kompilieren zu müssen.
Romain Verdier

1

Einfache Kombination von Teilkonfigurationen zu einer endgültigen vollständigen Konfiguration.

In Webanwendungen werden beispielsweise Modell, Ansicht und Controller normalerweise in separaten Konfigurationsdateien angegeben. Verwenden Sie den deklarativen Ansatz, den Sie beispielsweise laden können:

  UI-context.xml
  Model-context.xml
  Controller-context.xml

Oder laden Sie mit einer anderen Benutzeroberfläche und einigen zusätzlichen Controllern:

  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

Um dasselbe im Code zu tun, ist eine Infrastruktur zum Kombinieren von Teilkonfigurationen erforderlich. Nicht unmöglich im Code, aber sicherlich einfacher mit einem IoC-Framework.


1

Oft ist der wichtige Punkt, wer die Konfiguration ändert, nachdem das Programm geschrieben wurde. Bei der Konfiguration im Code gehen Sie implizit davon aus, dass die Person, die ihn ändert, über dieselben Fähigkeiten und denselben Zugriff auf den Quellcode usw. verfügt wie der ursprüngliche Autor.

In Produktionssystemen ist es sehr praktisch, eine Teilmenge von Einstellungen (z. B. Alter in Ihrem Beispiel) in eine XML-Datei zu extrahieren und z. B. Systemadministratoren oder Supportmitarbeitern zu erlauben, den Wert zu ändern, ohne ihnen die volle Macht über den Quellcode oder andere Einstellungen zu geben - oder nur um sie von Komplexität zu isolieren.


Das ist ein gültiger Punkt, aber die Federkonfiguration ist oft recht komplex. Obwohl es einfach ist, das Alter zu ändern, muss sich sysadmin immer noch mit großen XML-Dateien befassen, die er nicht unbedingt vollständig verstehen muss. Ist es nicht besser, Teile, die konfiguriert werden sollen, in etwas noch Einfacheres zu extrahieren als Spring XML Config? Wie die Eigenschaftendatei, mit einer einzelnen Zeile "age = 23", und lassen Sie den Administrator keine anderen Details wie Klassennamen usw. ändern, die Kenntnisse der internen Programmstruktur erfordern.
Pavel Feldman

Ich habe kürzlich an einem Projekt gearbeitet, das eine Mischung aus Java-Code und XSLT enthält. Das Team war eine Mischung aus Leuten, die stark in Java waren (und vielleicht weniger gut mit XML und XSLT arbeiten konnten). und Leute, die sehr stark mit XML und XSLT gearbeitet haben (und mit Java weniger vertraut sind). Da die Konfiguration von der zweiten Gruppe verwaltet werden sollte, war es sinnvoll, Spring zu verwenden und XML-Konfigurationen zu verwenden. Mit anderen Worten, Spring löste ein Problem der Arbeitsteilung im Team. Es hat kein "technisches" Problem gelöst; in dem Sinne, dass die Konfiguration genauso gut mit Java-Code hätte durchgeführt werden können.
Dawood ibn Kareem

Wann würde "Support-Personal" etwas darüber wissen, dass einige Klassen, die in einem Abhängigkeitsinjektionscontainer erstellt werden, geändert werden müssen? Wirklich unter der Annahme, dass es die Aufgabe eines Entwicklers ist, dies zu tun?
Jimbo

Genau aus diesem Grund ist das Extrahieren der Konfigurationswerte (z. B. der URL des Systems, in das Sie integrieren) sinnvoll: Das Support-Personal bearbeitet die Eigenschaftendatei oder (im schlimmsten Fall) die XML-Datei, die kompilierte Java-Klasse bleibt gleich.
Miro A.

1

Aus Sicht des Frühlings kann ich Ihnen zwei Antworten geben.

Erstens ist die XML-Konfiguration nicht die einzige Möglichkeit, die Konfiguration zu definieren. Die meisten Dinge können mithilfe von Anmerkungen konfiguriert werden, und die Dinge, die mit XML erledigt werden müssen, sind Konfigurationen für Code, den Sie sowieso nicht schreiben, wie z. B. einen Verbindungspool, den Sie aus einer Bibliothek verwenden. Spring 3 enthält eine Methode zum Definieren der DI-Konfiguration mit Java, ähnlich der handgerollten DI-Konfiguration in Ihrem Beispiel. Die Verwendung von Spring bedeutet also nicht, dass Sie eine XML-basierte Konfigurationsdatei verwenden müssen.

Zweitens ist Spring viel mehr als nur ein DI-Framework. Es verfügt über viele weitere Funktionen, einschließlich Transaktionsmanagement und AOP. Die Spring XML-Konfiguration mischt alle diese Konzepte zusammen. Oft spezifiziere ich in derselben Konfigurationsdatei Bean-Abhängigkeiten, Transaktionseinstellungen und füge Beans mit Sitzungsbereich hinzu, die tatsächlich mit AOP im Hintergrund verarbeitet wurden. Ich finde, dass die XML-Konfiguration einen besseren Ort bietet, um all diese Funktionen zu verwalten. Ich bin auch der Meinung, dass die auf Anmerkungen basierende Konfiguration und die XML-Konfiguration besser skaliert werden können als die Java-basierte Konfiguration.

Aber ich verstehe Ihren Standpunkt und es ist nichts Falsches daran, die Konfiguration der Abhängigkeitsinjektion in Java zu definieren. Normalerweise mache ich das selbst in Unit-Tests und wenn ich an einem Projekt arbeite, das klein genug ist, dass ich kein DI-Framework hinzugefügt habe. Normalerweise spezifiziere ich keine Konfiguration in Java, da dies für mich die Art von Installationscode ist, den ich nicht schreiben möchte, wenn ich mich für Spring entschieden habe. Dies ist jedoch eine Präferenz. Dies bedeutet jedoch nicht, dass die XML-Konfiguration der Java-basierten Konfiguration überlegen ist.


0

Spring hat auch einen Eigenschaftenlader. Mit dieser Methode setzen wir umgebungsabhängige Variablen (zB Entwicklung, Test, Akzeptanz, Produktion, ...). Dies könnte beispielsweise die Warteschlange sein, die abgehört werden soll.

Wenn es keinen Grund gibt, warum sich die Eigenschaft ändern würde, gibt es auch keinen Grund, sie auf diese Weise zu konfigurieren.


0

Ihr Fall ist sehr einfach und benötigt daher keinen IoC-Container (Inversion of Control) wie Spring. Wenn Sie dagegen "auf Schnittstellen programmieren, nicht auf Implementierungen" (was in OOP eine gute Praxis ist), können Sie folgenden Code haben:

IService myService;
// ...
public void doSomething() {
  myService.fetchData();
}

(Beachten Sie, dass der Typ von myService IService ist - eine Schnittstelle, keine konkrete Implementierung). Jetzt kann es praktisch sein, Ihren IoC-Container während der Initialisierung automatisch die richtige konkrete Instanz von IService bereitstellen zu lassen. Wenn Sie über viele Schnittstellen und viele Implementierungen verfügen, kann es mühsam sein, dies von Hand zu tun. Hauptvorteile eines IoC-Containers (Dependency Injection Framework) sind:

  • Externe Konfiguration der Zuordnung zwischen Schnittstellen und ihren konkreten Implementierungen
  • Der IoC-Container behandelt einige knifflige Probleme wie das Auflösen komplizierter Abhängigkeitsdiagramme, das Verwalten der Lebensdauer von Komponenten usw.
  • Sie sparen Codierungszeit, da Sie Zuordnungen deklarativ und nicht in einem Prozedurcode bereitstellen
  • Das Inversion of Control-Prinzip ermöglicht ein einfaches Testen von Einheiten, da Sie echte Implementierungen durch gefälschte ersetzen können (z. B. das Ersetzen der SQL-Datenbank durch eine In-Memory-Datenbank).

0

Das Initialisieren in einer XML-Konfigurationsdatei vereinfacht Ihre Debugging- / Anpassungsarbeit mit einem Client, auf dessen Computern Ihre App bereitgestellt ist. (Weil es keine Neukompilierung + Ersetzen von Binärdateien erfordert)


-2

Einer der attraktivsten Gründe ist das " Hollywood-Prinzip ": Rufen Sie uns nicht an, wir rufen Sie an. Eine Komponente ist nicht erforderlich, um die Suche nach anderen Komponenten und Diensten selbst durchzuführen. stattdessen werden sie automatisch bereitgestellt. In Java bedeutet dies, dass keine JNDI-Lookups mehr innerhalb der Komponente durchgeführt werden müssen.

Es ist auch viel einfacher, eine Komponente einzeln zu testen: Anstatt ihr eine tatsächliche Implementierung der benötigten Komponenten zu geben, verwenden Sie einfach (möglicherweise automatisch generierte) Mocks.


Bei dieser Antwort geht es um die Abhängigkeitsinjektion. Ich weiß, was es ist, ich weiß, dass es gut ist und ich sage es klar im ersten Satz der Frage. Die Frage betraf die Vorteile der DI-Konfiguration im Vergleich zum einfachen Initialisierungscode.
Pavel Feldman

Beantwortet die Frage nicht wirklich
Casebash
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.