Dependency Injection?

Status
Nicht offen für weitere Antworten.
N

N/A

Gast
hi, kann mir mal jemand genau erklären, wie DI funktioniert? hab mir da schon einige beispiele angesehen, aber so richtig hab ichs nicht geschnallt :-|.

vielen dank:)
 
A

Anmeldeboykottierer

Gast
Hi,
eigentlich ist die Idee hinter DI sehr einfach. Dieses Muster ähnelt (stark) dem Fabrikmuster.
In der OOP erzeugst du ja Klassen. Ziel ist es auch, dass diese nicht unnötig aufgebläht werden sondern möglichst einfach sind und genau für die Lösung eines Problems. Das führt zu modularem, leicht wartbaren Code, der aber in sehr vielen Klassen abgelegt wird.
Wenn du jetzt mit einer Klasse arbeitest, so hat sie häufig ein paar Abhängigkeiten. Sagen wir mal du schreibst deinen eigenen Compiler, dann muss ein File eingelesen werden, in Token zerlegt und diese dann geparst werden...
Schon diese paar Schritte gehören in eigene Klassen. Schließlich kann sich etwas am Parser ändern, ohne dass du dazu wissen muss wie eine Datei in Token zerlegt wird.
Dennoch hast du eine Menge Abhängigkeiten. Damit dein Compiler funktionieren kann, muss er geparste Token bekommen. Sauber OOP ist es hier schonmal, wenn er diese Abhängigkeit als Interface regelt (er möchte eine Instanz die ein bestimmtes Interface implementiert). Dummerweise muss er jetzt zum Erzeugen einer solchen Instanz aber auch eine konkrete Implementierung dieses Interfaces kennen. Das heißt, hier ist eine Abhängigkeit vorhanden.
Bei DI wird diese Abhängigkeit aufgelöst. Wo die Instanz herkommt und welcher Konkreten Implementierung sie angehört ist oft nicht nötig zu wissen. Hier wird eine äussere Klasse aufgerufen, die die konkreten Instanzen erzeugt und deiner Klasse zuweist.
Um beim Beispiel zu bleiben, ITokenizer ist ein Interface, dass ein Funktion getToken() bietet. Dein Parser braucht nur diese Methode, interessiert sich aber nicht wie die Token erzeugt werden. Da du aber ein ITokenizer brauchst, der dir die Token liefert, erzeugt sich dein Parser eben eine Instanz (und muss dazu die entsprechende Klasse kennen).
Mit DI geht das dann anders, hier hast du etwas aussen rum, der erzeugt eine Instanz TokenizerA oder TokenizerB oder was auch immer, die ITokenizer implentiert und übergibt die per setter an die ebenfalls hier erzeugte Instanz von Parser. Die einzelnen Klassen müssen sich also nicht mehr kennen. Nur eine hat "den Überblick" und erzeugt alle Instanzen und weißt diese zu.

Bei DI werden aber eben nur solche Abhängigkeiten aufgelöst, also Instanzen erzeugt und zugewiesen. Imho wird (anders als bei einer Fabrik) dabei keine Initialisierung vorgenommen.

Gruß Der Anmeldboykottierer
 
N

N/A

Gast
hi, klingt ja wirklich bach factory... ich packe also den service, den tokenizer in ein interface, richtig? das gibts einen zentralen punkt, der alles anlegt. der macht dann die injection. hab ichs jetzt?

danke:)
 
G

Gast

Gast
Ja, ich denke jetzt hast du es! :)
Wie gesagt, das Pattern zielt komplett auf eine "externe" Erzeugung ab (die dann injeziert wird)
 

Jouhni

Mitglied
Seid ihr euch sicher, dass das "Drumherum" das Objekt lediglich erzeugt und anschließend injeziert? Ich meine, dass der Service komplett konfigurierte Objekte injeziert.

Gruß,
Jouhni
 
A

Anmeldeboykottierer

Gast
Hi,
ich bin mir ziemlich sicher, dass die Objekte die injeziert werden nur instanziert werden müssen. Alles andere (also auch Kombinationen mit z.B. einer Fabrik die gleich initialisiert) ist natürlich möglich, wird aber nicht vom Pattern "gefordert".
Das eigentliche Problem dass hier gelöst werden soll ist die Abhängigkeit von konkreter Klasse und Interface / abstrakter Klasse.
Und wenn man ein Objekt bekommt und dessen Interface kennt, kann man schließlich auch selbst initialisieren (soweit das Interface dafür geeignete Methoden bereit stellt).

Gruß Der Anmeldeboykottierer
 

EOB

Top Contributor
hi, was meinst du mit abhaengigkeit von interface/abtrakter klasse? in der klasse, in welche injiziert wird, ist ja trotzdem das interface drin. man mach ja sowas wie:

Code:
class A{
InterfaceTyp it; // -->abhaengigkeit??

    //hier wird injiziert
    public inject(InterfaceTyp it){
        this.it=it;
    }

    //methoden von it aufrufen
    .....
}

und dann eben da wo injiziert wird sowas:

Code:
class Injecter{

    A a = new A();

    InterfaceTyp it = new KlasseDieInterfaceTypImplementiert();

    a.inject(it);
}

trotzdem ist man ja aber von InterfaceTyp abhaengig...oder wie jetzt? ohne haare spalten zu wollen :). du meinst doch sicher von einer konkrteten implementierung eines interfaces/einer abstrakten klasse, oder? ist nur fuer mein verstaendniss ;).

gruese:)
 
A

Anmeldeboykottierer

Gast
Ich meinte die Abhängigkeit, dass du (wie Du richtig sagst) eine konkrete Implementierung zu einem Interface / einer abstrakten Klassen kenne müsstest.
Um hier Missverständnissen vorzubeugen, ein kleines Beispiel:
Code:
// ohne Injection:

interface B {

  void doFoo();

} 

class A {

  private B b;

  public A() {
    this.b = new BImplementierung();
  }
}

Hier siehst du einfach, dass zwar sauber abstrakt gearbeitet werden soll, indem eine Variable vom Interface Typ B ist. Hier möchte man also die Vorteile der Abstraktion nutzen. Dummerweise muss die Instanz von B jetzt irgendwo her kommen. Ohne jegliches Pattern (oder andere Workarounds) muss jetzt also eine konkrete Implementierung bekannt sein und dass macht eigentlich schon die Vorteile der Abstraktion (teilweise) zu nichte.
Einfach austauschen ist nicht, immerhin muss man eine weitere konkrete Implementierung kennen und wissen wie man diese erzeugt...

Da kommt das Pattern ins Spiel. Es löst einfach dieses Problem, indem A keine konkrete Klasse kennen muss. Eine fertige Instanz wird einfach "extern" erzeugt und irgendwie in A injiziert. Die Frage wie das passiert ist dann natürlich ein Implementierungsdetail das variieren kann (und darf).
Code:
public class A {
  private B b;
  
  protected void inject(B b) {
    this.b = b
  }

}

Hier muss A nun wirklich keine Klasse kennen die B implementiert und damit auch nicht wissen wie ein B erzeugt wird.

Das ist dann auch alles, was das Pattern aussagt. Ob die Instanz hier erzeugt ist oder initialisiert oder oder oder ist nicht wirklich Bestandteil des Pattern.
Natürlich bietet sich die Kombination mit anderen Pattern an! Aber wie bereits gesagt, nicht geregelt in diesem Pattern (warum auch künstl. einschränken?)
 

EOB

Top Contributor
hi, also dann haben wir das jetzt geklaert :). genau so meinte ich das auch!

dank und gruesse aus nordland ;)
 
Da das Thema ja momentan immer noch heiß diskutiert wird, moechte ich es gerade mal wieder nach oben schieben ;)
Meine Frage ist - wie wird DI normalerweise implementiert? Handelt es sich um ein "normales" Pattern (das man mit Java 1.4 implementieren kann) oder braucht man dafuer eine bestimmte Java Version oder sogar einen Container, der die Instanzierung der notwendigen Klassen uebernimmt und an der entsprechenden Stelle einschleust?

Danke
Georg
 

SnooP

Top Contributor
Naja - du brauchst ja einen Mechanismus, der genau das abnimmt was oben diskutiert wurde - irgendwas, welches die Zuordnung konkrete Klasse auf Interface hinbekommt. Sprich das injezieren von konkretem Code ;) ... dies kann man evtl. selbst schreiben per Reflection und einer eigenen Konfigurationsdatei, die ausgelesen wird und so zur Laufzeit entsprechende Abhängigkeiten bzw. Objekte in die dafür vorgesehenen Klassen injeziert... - das ist aber doch eher umständlich. Daher nimmt man da gängigerweise entsprechende Frameworks für. Das bekannteste... hmm... und das einzig mir wirklich bekannte allgemeine *g* - Framework ist hier Spring. In der spring-config wird dann zentral gesteuert welche Klassen per DI behandelt werden müssen.

Wenn man JSF verwendet, werden die Backing-Beans ebenfalls per DI injeziert... das geschieht hier aber eigentlich noch transparenter, weil man sich als Anwender von JSF darüber nicht soo wirklich Gedanken macht. Die Controller-Klassen, Backing-Beans sind halt einfach da...
 
Hab ich verstanden. Also den Vorgang. :)

Was ich noch nicht so 100%ig verstanden habe ist der Nutzen, den man aus der Erzeugung zur Laufzeit zieht.
Wenn ich das Beispiel oben betrachte, gibt es ja hier fuer eine Methode inject(B meinB) {} ja schon eine bekannte Schnittstelle insofern als hier festgelegt ist, dass es sich bei der zu injizierenden Klasse --bzw. seiner Instanz-- um ein B handelt.
An welcher Stelle wird reflect fuer gewoehnlich benutzt?
 

SnooP

Top Contributor
Das ist richtig - auf der Seite deines Codes ist die Schnittstelle klar - inject(B deinB) bzw. häufig einfach eine set-Methode für den zu injizierenden Service (bzw. des Interfaces dessen *g*) ist die Anbindung aus der Sicht deiner Anwendung. Aaaaaber... an irgendeiner Stelle muss ja jetzt ein konkreter Service instanziert werden: new DeinBJetztKonkret(). Und dieser Vorgang des konkreten Instanzierens und aufrufens der jeweiligen injizier-Methode (setter oder auch sogar über einen Konstruktor geht in Spring beides) wird durch DI übernommen. Gleichzeitig kann über so ein Framework und externer Konfiguration natürlich auch spezielles Verhalten definiert werden, wie z.B. lazy-load etc...

Ein klarer Vorteil ist bei so einer losen Kopplung, dass man Services jederzeit austauschen kann... im besten Fall sogar ohne "Programmeinsatz" sprich im laufenden Betrieb, xml-datei verändern und fertig ;)
 
Ahhh now I see. Sozusagen, eine Art "ausgelagerte Strategie (i.S.v. Strategie-Pattern)", um die Austauschbarkeit zu gewaehrleisten. Das ist natuerlich sehr geschickt. :)
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben