PDF 101K

Werbung
Dokumentation
zur
Ausarbeitung des Artikels von
Martin Fowler
Inversion of Control Containers and
the Dependency Injection pattern
http://martinfowler.com/articles/injection.html
von
André Kley – 269767
Julian Päuler – 270140
11.12.2006
1.Martin Fowler
2.Einleitung
3.IoC / Dependency Injection
3.1 Constructor Injection
3.2 Setter Injection
3.3 Interface Injection
4.Service Locator
4.1 Nutzung eines abgetrennten Interfaces
4.2 Dynamischer Service Locator
4.3 Nutzung von Locator und Injection
5. Service Locator vs Dependency Injection
6. Constructor vs Setter Injection
7. Fazit
8. Eigene Beispiele
9. Quellen
1. Martin Fowler (Folie 3)
Martin Fowler ist Autor und renommierter Referent zum Thema Softwarearchitektur,
spezialisiert auf objekt-orientierte Analyse und Design, UML, Entwurfsmuster und agile
Softwareentwicklung. Er schrieb fünf bedeutende Bücher zum Thema Softwareentwicklung.
Heute arbeitet er als Chefentwickler beim Consulting-Unternehmen ThoughtWorks.
2. Einleitung (Folie 4/5)
Ein generelles Problem in der Softwareentwicklung ist die Art der Zusammenführung
unterschiedlicher Elemente:
Wie bekommt man diese Web Controller Architektur mit einem Datenbank Interface unter
einem Dach, wenn beide von unterschiedlichen Teams entwickelt wurden, die sich
gegenseitig nicht kennen.
class MovieLister...
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
Der Kern des Artikels ist dieses „finder“ Objekt oder die Art wie man das „lister“ Objekt mit
diesem bestimmten „finder“ Objekt verbindet. Der Grund warum dies interessant ist, ist dass
man die „moviesDirectedBy“ Methode komplett unabhängig von der Speicherung der Filme
nutzen möchte.
Somit ist alles was diese Methode macht ein Verweis auf einen „finder” zu legen und alles
was der „finder” macht, ist zu wissen wie man auf die „findAll” Methode zu antworten hat.
Dies kann man hervorheben durch die Definition eines Intefaces für den „finder“:
public interface MovieFinder {
List findAll();
}
Nun ist alles sehr gut entkoppelt, aber letztendlich muss man eine konkrete Klasse einführen,
die die Filme herausgibt:
class MovieLister...
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}
Wenn man diese Klasse nur für sich nutzt, ist alles schön und gut. Wenn aber jemand anderes
von dieser Funktion Gebrauch machen möchte, aber seine Filmlisten komplett anders
abspeichert (z.b.: über eine SQL Datenbank, eine XML Datei, einen Web Dienst, oder einfach
ein anderes Format von einer Textdatei), benötigt man eine andere Klasse die diese Daten
erfasst.
Da bereits eine „MovieFinder“ Schnittstelle definiert ist, braucht man die
„moviesDirectedBy“ Methode nicht zu ändern. Aber man braucht immer noch eine Art und
Weise wie man eine Instanz der richtigen „finder“ Implemenation integriert, die nur noch von
der Schnittstelle abhängig ist.
Man benötigt eine Implementationsklasse vom „finder“ die nicht beim Kompilieren
eingebunden wird. Stattdessen soll der „lister“ mit allen jetzigen und zukünftigen
Implementierungen zusammenarbeiten. Das Problem dabei ist, wie erstellt man diese
Verknüpfung, so dass die „lister” Klasse die Implementierungsklasse ignoriert, aber dennoch
mit den Instanzen kommunizieren kann.
Man benötigt dafür Plugins, die Dienste gewährleisten, so dass man unterschiedliche
Implemenationen in unterschiedlichen Verwendungen nutzen kann.
Somit ist das Kernproblem, die Einbindung dieser Plugins in die Applikation. Dies wird
gelöst durch die Umkehrung der Kontrolle („Inversion of Control”, IoC).
Frei nach dem Motto: Don’t call us, we call you. (Hollywood Prinzip)
3. IoC / Dependency Injection (Folie 6)
Frühere Benutzerschnittstellen wurden durch die Applikation kontrolliert. Man hatte dort eine
Sequenz von Kommandos wie „Name eingeben”, „Adresse eingeben”. Das Programm führte
durch die Eingaben und erwartete die entsprechenden Antworten. Durch grafische
Benutzeroberflächen beinhaltete das Benutzerschnittstelle Framework diese Schleifen, das
Programm hingegen liefert Ereignisabfragen für die verschiedenen Felder des Dialogs. Die
Hauptkontrolle das Programms wurde umgekehrt, weg von einem, hin zum Framework.
Für diese neue Art von Containern ist die Unkehrung, die Art wie sie eine Plugin
Implementation suchen. In diesem Beispiel hat der „lister“ die „finder“ Implementation durch
direktes Ansprechen gefunden. Dies unterscheidet den „finder“ vom Plugin. Der Ansatz den
diese Container verfolgen ist die Sicherstellung, dass jeder Nutzer des Plugins bestimmten
Konventionen folgt, die es einem separaten Assembler-Modul erlauben die Implementation in
den „lister“ einzubinden.
Als Resultat vieler Diskussionen mit IoC Vertretern hat man sich auf den Namen
„Dependency Injection” (Unabhängige Injizierung) geeinigt, da der Name IoC viel zu
allgemein gehalten ist.
3.1 Constructor Injection (Folie 7/8/9)
In den aktuellen IoC Diskussionen wird es auch als Typ 3 bezeichnet.
PicoContainer benutzen einen Konstruktor um zu entscheiden, wie sie eine „finder”
Implementierung in die „lister” Klasse injizieren. Für diese Aufgabe muss die „movie lister”
Klasse einen Konstruktor deklarieren, der alles beinhaltet was injiziert werden muss.
class MovieLister...
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
Der „finder” wird ebenfalls vom PicoContainer gesteuert und injiziert somit den Dateinamen
der Textdatei mittels eines Containers.
class ColonMovieFinder...
public ColonMovieFinder(String filename) {
this.filename = filename;
}
Der PicoContainer braucht dann nur noch die Anweisung welche Implementierungsklasse mit
welcher Schnittstelle verbunden werden soll und welche Zeichenkette in den „finder” injiziert
werden soll.
private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
Parameter[] finderParams = {new ConstantParameter("movies1.txt")};
pico.registerComponentImplementation(MovieFinder.class,
ColonMovieFinder.class, finderParams);
pico.registerComponentImplementation(MovieLister.class);
return pico;
}
Dieser Konfigurations-Code wird typischerweise in einer anderen Klasse festgehalten. Für
dieses Beispiel schreibt jeder der den „lister“ nutzen möchte, den entsprechenden
Konfigurations-Code in eine eigene Setup Klasse. Man könnte die Konfigurationsinformationen natürlich auch in eine separate Datei speichern, die dann von einer Klasse
eingelesen wird. Der PicoContainer bietet das nicht, aber ein es eng verwandtes Projekt
namens NanoContainer, welches die entsprechenden Funktionen mitbringt, um eine XML
Konfigurationsdatei zu nutzen. Solche NanoContainer parsen den XML und konfigurieren
den hinterlegten PicoContainer. Die Philosophie des Projektes ist die Trennung des
Konfigurationsdateiformates von dem zugrunde liegenden Mechanismus.
PicoContainer unterstützen nicht nur die Constructor Injection, sondern auch die Setter
Injection. Dennoch bevorzugen die Entwickler Constructor Injection.
3.2 Folie 10/11/12: Setter Injection mit Spring
In den aktuellen IoC Diskussionen wird es auch als Typ 2 bezeichnet.
Das Spring Framework ist ein weitreichendes Framework für die Java Enterprise
Entwicklung. Es beinhaltet eine Abstraktionsschicht für Transaktionen, persistente
Frameworks, Web-Anwendungsentwicklung und JDBC. Wie PicoContainer unterstützt es
beides: Constructor und Setter Injection. Aber die Entwickler bevorzugen die Setter Injection.
Um den „movie lister” die Injektion zu ermöglichen, definiert man eine setting-Methode
(Setter). Ebenso definiert man einen Setter für den String des „finders“.
class MovieLister...
private MovieFinder finder;
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
class ColonMovieFinder...
public void setFilename(String filename) {
this.filename = filename;
}
Der dritte Schritt ist die Konfiguration der Dateien. Spring unterstützt die Konfiguration
durch XML Dateien und auch durch Code, aber XML ist der bevorzugte Weg
<beans>
<bean id="MovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>
3.3 Folie 13: Interface Injection
In den aktuellen IoC Diskussionen wird es auch als Typ 1 bezeichnet.
Die dritte Injection-Technik ist die Definierung und Nutzung von Interfaces (Schnittstellen)
für die Injektion.
Zu Beginn dieser Technik definiert man eine Schnittstelle über welche man die Injektion
durchführt. Hier ist die Schnittstelle um den „movie finder“ in ein Objekt zu injizieren.
public interface InjectFinder {
void injectFinder(MovieFinder finder);
}
Diese Schnittstelle wird nun durch denjenigen definiert, der auch die „MovieFinder“
Schnittstelle liefert. Sie muss durch jede Klasse implementiert sein, die einen „finder“ nutzen
will, wie z.B. auch der „lister”.
class MovieLister implements InjectFinder...
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}
Der gleiche Ansatz wird genutzt um einen Dateinamen in die „finder” Implementation zu
injizieren:
public interface InjectFinderFilename {
void injectFilename (String filename);
}
class ColonMovieFinder implements MovieFinder, InjectFinderFilename...
public void injectFilename(String filename) {
this.filename = filename;
}
Nun noch Konfigurations-Code um die Implementierung zu integrieren. Zuerst wie gehabt die
Komponenten registrieren. Danach ein neuer Schritt: Jedes Interface benötigt Code für die
Injektion der abhängigen Objekte. Dies geschieht durch Registrierung der Injector Objekte
mit den Containern.
class Tester...
private void registerComponents() {
container.registerComponent("MovieLister", MovieLister.class);
container.registerComponent("MovieFinder", ColonMovieFinder.class);
}
private void registerInjectors() {
container.registerInjector(InjectFinder.class,
container.lookup("MovieFinder"));
container.registerInjector(InjectFinderFilename.class, new
FinderFilenameInjector());
}
Bei der Interface Injection muss für jede Art der Abhängigkeit eine Schnittstelle definiert
werden. Diese Notwendigkeit entfällt bei den anderen Varianten.
4. Service Locator (Folie 14/15)
Der Hauptvorteil einer “Dependency Injection” ist, dass sie die Abhängigkeit der
„MovieLister“ Klasse von der konkreten „MovieFinder“ Implementation löst. Das erlaubt mir
die Weitergabe und die Implementation in passende Implemenatationen anderer
Umgebungen. Aber Injektion ist nicht der einzige Weg diese Abhängigkeit zu beseitigen, ein
Weiterer ist die Nutzung eines Service Locator.
Die Grundidee hinter einem Service Locator besteht in einem Objekt, dass alle Dienste
beschafft, die die Applikationen benötigen könnte. Ein Service Locator für diese Applikation
hätte eine Methode die einen „movie finder“ zurückgibt, wenn dieser benötigt wird. Natürlich
löst dies nicht das Grundproblem, man muss weiterhin den Locator in den Lister bekommen,
was zu den folgenden Abhängigkeiten führt:
In diesem Beispiel ist der Service Locator eine einfache Registry. Der „lister“ kann diese
nutzen, um den „finder“ zu bekommen , sobald er instanziert wird:
class MovieLister...
MovieFinder finder = ServiceLocator.movieFinder();
class ServiceLocator...
public static MovieFinder movieFinder() {
return soleInstance.movieFinder;
}
public static void load(ServiceLocator arg) {
soleInstance = arg;
}
public ServiceLocator(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
private static ServiceLocator soleInstance;
private MovieFinder movieFinder;
Der Service Locator muss dann nur noch konfiguriert werden:
private void configure() {
ServiceLocator.load(new ServiceLocator(new
ColonMovieFinder("movies1.txt")));
}
4.1 Nutzung eines abgetrennten Interfaces für den Locator (Folie 16)
Eins der Probleme des obigen Ansatzes ist, dass der „MovieLister“ abhängig von der
kompletten ServiceLocator Klasse ist, obwohl er nur einen einzigen Dienst nutzt. Man kann
dieses Problem reduzieren, indem man ein Interface zwischen dem MovieLister und dem
Service Locator schiebt.
Somit kann der „lister“ seinen Teil vom Service Locator ansprechen, ohne ihn komplett
nutzen zu müssen.
In diesem Fall stellt der Provider des „listers“ auch eine Locator Schnittstelle bereit, die er
benötigt um den „finder“ zu bekommen. Der Locator muss dann diese Schnittstelle
implementieren um Zugang zu dem „finder“ bereitzustellen.
4.2 Dynamischer Service Locator (Folie 17)
Das obige Beispiel war statisch, da die ServiceLocator Klasse Methoden für jeden Dienst
besaß den sie benötigte. Das ist aber nicht der einzige Weg dies zu tun. Man kann ebenfalls
einen dynamischen Service Locator erstellen, der einem erlaubt jeden Dienst, den man
benötigen könnte, zu integrieren und während der Laufzeit auszuwählen.
Dazu benutzt der ServiceLocator eine HashMap, die die Services enthält. Sie bietet
generische Methoden zum Erhalten und Laden der Dienste an. Das Konfigurieren umfasst
dann das Laden eines Dienstes mit einem entsprechenden Schlüssel.
Im Großen und Ganzen ist dieser Ansatz sicherlich flexibel aber nicht sehr explizit. Die
einzige Art herauszufinden wie man einen Service erreicht ist die Nutzung von
Textschlüsseln. Bei expliziten Methoden ist es einfacher diese zu finden, nämlich durch
Nachschauen in den Schnittstellendefinitionen.
4.3 Nutzung von Locator und Injection (Folie 18)
„Dependency Injection” und Sercice Locator sind keine sich gegenseitig ausschließende
Konzepte. Ein gutes Beispiel beide zusammen zu nutzen ist das Avalon Framework. Avalon
nutzt einen Service Locator, aber benutzt Injektion um den Komponenten mitzuteilen wo sie
den Locator finden können.
public class MyMovieLister implements MovieLister, Serviceable {
private MovieFinder finder;
public void service( ServiceManager manager ) throws ServiceException {
finder = (MovieFinder)manager.lookup("finder");
}
Die Service-Methode ist ein Beispiel für die Interface Injection, die dem Container erlaubt
einen Service Manager in „MyMovieLister“ zu injizieren. Der ServiceManager ist ein
Beispiel für einen Service Locator. Hier speichert der Lister den Manager nicht in einem Feld,
stattdessen benutzt er ihn um den finder direkt zu speichern.
5. Service Locator vs Dependency (Folie 19)
Beide Implementationen stellen das fundamentale Entkoppeln bereit und bei beiden ist der
Applikationscode unabhängig von der konkreten Implementation des Service Interfaces.
Service Locator:
- die Applikations Klasse fragt explizit den Locator.
- jeder Nutzer ist Abhängig vom Locator
- um die Abhängigkeiten zu sehen, muss man den Code durchsuchen
Dependency Injection:
- es gibt keine explizite Anfrage, der Service erscheint in der Applikations Klasse – durch
Inversion of Control.
- schwieriger zu debuggen (Erfahrungswert von M.F.)
- man sieht die Abhängigkeiten einfacher (Im Konstrukor oder bei den Set-Methoden)
Wenn man nun verschieden Klassen entwirft, die auf einen Service zugreifen sollen, ist es
nicht schwer den Service Locator zu benutzen. Wenn man im Beispiel den „MoveLister“
weitergeben möchte, ist das kein Problem. Man muss dann nur den Locator so konfigurieren,
dass man die richtige Service Implementation wählt. (Per Code oder Datei) Hierbei ist
Dependency Injection nicht wirklich notwendig.
Der Unterschied ist, wenn man seine „lister“ Klasse an eine Anwendung anbinden möchte,
die von anderen geschrieben wurde. Denn dann kennt man die API des Service Locator nicht.
Jeder Kunde könnte seinen eigenen nicht kompatiblen Service Locator besitzen. Man könnte
um das Problem herum kommen, indem man ein abgetrenntes Interface nutzt. Jeder Kunde
müsste dann einen Adapter schreiben, der das Interface an seinen Locator bindet. Aber auf
jeden Fall muss ich zuerst den Service Locator sehen um meine spezifiziertes Interface zu
binden. Dadurch beginnt die Einfachheit der direkten Verbindung zu einem Locator zu
schwinden.
6. Constructor vs Setter (Folie 20)
Sowohl mit der Constructor Injection als auch mit der Setter Injection ist es sehr einfach
Bindungen zwischen Komponenten herzustellen. Man muss nichts kurioses in den
Komponenten machen und es ist einfach für den Injector alles zu konfigurieren.
Bei der Interface Injection hingegen muss man sehr viele Interfaces schreiben. Sie ist dadurch
zwar flexibler aber wird nur bei ganz wenigen Containern angewandt (Avalon). Gerade
wegen diesem großen Aufwand verwenden heutzutage die meisten lightweight Container
Setter oder Constructor Injection.
Constructor Injection:
- jedes Objekt ist sofort verfügbar
- geringfügig weniger Code
- unveränderbare Felder können versteckt werden
Setter:
- keine langen Argumentenlisten die unübersichtlich werden könnten
- Setter bekommen eindeutige Namen (Bei Konstruktoren müssen die Variablen an der
richtigen Position stehen)
- Konstruktoren werden nicht automatisch vererbt
7. Fazit (Folie 21)
Beim entwicklen von Anwendungsklassen sind Service Locator und Dependency
Injection fast gleich.
M.F. empfiehlt den Service Locator zu nutzen, es sei denn man entwickelt mehrere Klassen
die in verschiedene Anwendungen laufen sollen.
Nutzt man Dependency Injection empfiehlt es sich mit der Constructor-Injection zu
starten. Man sollte aber bereit sein auf Setter Injection umzustellen, falls es zu oben
genannten Problemen kommt.
8. Eigene Beispiele
Beispiele zur Dipendency Injection
Voraussetzung: Java SDK/JDK muss installiert sein
Constructor Injection mit PicoContainern
•
•
•
Download der picocontainer.jar auf http://www.picocontainer.org/Downloads
Diese .jar Datei muss in den Java Installation Pfad kopiert werden, unter ...\lib\ext
Wie folgt müssen jetzt die Java Klassen erstellt werden.
Girl.java
public class Girl {
Kissable kissable;
public Girl(Kissable kissable) {
this.kissable = kissable;
}
public void kissSomeone() {
kissable.kiss(this);
}
}
Diese Klasse wird die kiss Methode bei dem im Konstruktor regestriertem Interface
aufrufen.
Kissable.java
public interface Kissable {
void kiss(Object kisser);
}
Boy.java
public class Boy implements Kissable {
public void kiss(Object kisser) {
System.out.println("I'm a Boy and I was kissed by a " + kisser);
}
}
OldWoman.java
public class OldWoman implements Kissable {
public void kiss(Object kisser) {
System.out.println("I'm an old woman and I was kissed by a " + kisser);
}
}
Die beiden Klassen Boy und OldWoman können jeweils am Interface registriert
werden (siehe unten).
testWithPico.java
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.defaults.*;
public class testWithPico{
// Zur Testausführung
public testWithPico(){
MutablePicoContainer pico = configureContainer();
Girl girl = (Girl) pico.getComponentInstance(Girl.class);
girl.kissSomeone();
}
// Anmelden der benötigten Klassen
private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
//pico.registerComponentImplementation(Kissable.class, Boy.class);
pico.registerComponentImplementation(Kissable.class,
OldWoman.class);
pico.registerComponentImplementation(Girl.class);
return pico;
}
public static void main(String args[])
{
testWithPico lala = new testWithPico();
}
}
Durch den Befehl registerComponentImplementation wird die jeweilige Klasse an
dem Interface registriert, bzw. die Girl Klasse wird hier am PicoContainer registriert
und bekommt später beim anlegen das Kissable Objekt per Konstruktor zugewiesen.
Je nachdem ob die Boy oder OldWoman Klasse registriert wird sieht dies dann so aus:
System ausgabe: I'm a Boy and I was kissed by a Girl
System ausgabe: I'm an old woman and I was kissed by a Girl
Setter Injection mit Spring
•
•
•
Download des Spring Frameworks auf http://www.springframework.org/download
Die .jar Dateien
o spring.jar aus spring-framework...\dist und die
o commons-logging.jar aus spring-framework...\lib\jakarta-commons
o müssen in den Java Installation Pfad kopiert werden, unter ...\lib\ext.
Wie folgt müssen jetzt die Java Klassen erstellt werden.
Girl.java
public class Girl {
Kissable kissable;
public void setKissable(Kissable kissable) {
this.kissable = kissable;
}
public void kissSomeone() {
kissable.kiss(this);
}
}
Anstelle des Konstruktor wird diese Klasse nun per Set Methode registriert.
TestWithSpring.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class testWithSpring{
// Zur Testausführung
public testWithSpring(){
ApplicationContext ctx = new
FileSystemXmlApplicationContext("conf.xml");
Girl girl = (Girl) ctx.getBean("Girl_id");
girl.kissSomeone();
}
public static void main(String args[])
{
testWithSpring lala = new testWithSpring();
}
}
An den restlichen Klassen ändert sich nichts. Die Konfiguration wird nun per XML
Datei definiert.
conf.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="Girl_id" class="Girl">
<property name="kissable">
<ref local="Kissable_id"/>
</property>
</bean>
<bean id="Kissable_id" class="Boy"> // hier OldWoman oder Boy
</bean>
</beans>
Die Girl Klasse wird unter der „id“ Girl_id registriert, die Boy Klasse unter der „id“
Kissable_id.
Die Girl Klasse hat Set Methode mit dem Namen „SetKissable“ daher muss
hier
durch „property name“ auch „kissable“ angegeben werden, dieses
Methode
bekommt ein Objekt vom Typ Kissable_id (also der Boy Klasse).
Die TestWithSpring.java ausführen, ausgabe wie oben.
Interface Injection
Die Java Klassen müssten wie folgt abgeändert werden bzw. ein Interface würde
hinzu kommen.
Diese Art der Injection ist anscheinend nicht sehr beliebt, bis auf das Hivemind
Framework http://hivemind.apache.org/ das die Interface Injection unterstützen soll,
ist uns kein weiteres Framework bekannt.
Girl.java
public class Girl implements InjectKissable{
Kissable kissable;
public void injectKissable(Kissable kissable){
this.kissable = kissable;
}
public void kissSomeone() {
kissable.kiss(this);
}
}
InjectKissable.java
public interface InjectKissable {
void injectKissable(Kissable kissable);
}
Service Locator
Wie folgt müssen die Java Klassen erstellt werden.
Girl.java
public class Girl {
Kissable kissable = ServiceLocator.getKissable();
public void kissSomeone() {
kissable.kiss(this);
}
}
ServiceLocator.java
public class ServiceLocator{
public ServiceLocator(Kissable kissable) {
this.kissable = kissable;
}
public static Kissable getKissable() {
return soleInstance.kissable;
}
public static void load(ServiceLocator arg) {
soleInstance = arg;
}
private static ServiceLocator soleInstance;
private Kissable kissable;
}
testWithSL.java
public class testWithSL{
// Zur Testausführung
public testWithSL(){
configure();
Girl girl = new Girl();
girl.kissSomeone();
}
// Anmelden der benötigten Klassen
private void configure() {
ServiceLocator.load(new ServiceLocator(new Boy()));
}
public static void main(String args[])
{
testWithSL lala = new testWithSL();
}
}
Die restlichen Klassen bleiben unverändert. In der configure Methode wird die Boy
Klasse am ServiceLocator angemeldet.
Die TestWithSL.java ausführen, ausgabe wie oben.
9. Quellen
http://martinfowler.com/articles/injection.html
http://www.springframework.org
http://www.picocontainer.org
Herunterladen