PDF 82K
Transcription
PDF 82K
IoC Containers and the Dependency Injection pattern University of Applied Sciences Ausarbeitung des Artikels von Martin Fowler Inversion of Control Containers and the Dependency Injection pattern http://martinfowler.com/articles/injection.html 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences Inhaltsverzeichnis 1. Martin Fowler 5. Service Locator vs Dependency Injection 2. Einleitung 6. Constructor vs Setter Injection 3. IoC / Dependency Injection 7. Fazit 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 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 1. Martin Fowler Martin Fowler ist Autor und renommierter Referent zum Thema Softwarearchitektur, spezialisiert auf objektorientierte Analyse und Design, UML, Entwurfsmuster und agile Softwareentwicklung. Er schrieb fünf bedeutende Softwareentwicklung. Bücher zum Thema Heute arbeitet er als Chefentwickler beim ConsultingUnternehmen ThoughtWorks. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 2. Einleitung Das generelle Problem ist die Art der Zusammenführung unterschiedlicher Elemente. Beispiel: class MovieLister... 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(newMovie[allMovies.size()]); } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 2. Einleitung Beispiel: MovieLister public interface MovieFinder { List findAll(); } class MovieLister... private MovieFinder finder; public MovieLister() { finder = new ColonDelimitedMovieFinder("movies1.txt"); } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3. IoC / Dependency Injection Im vorherigen Beispiel hat der „lister“ die „finder“ Implementation durch direktes Ansprechen gefunden. Dies unterscheidet den „finder“ vom Plugin. Durch ein separates Assemblermodul soll die Implementation in den „lister“ eingebunden werden. 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. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.1 Constructor Injection Am Beispiel: PicoContainer (Komponenten definieren) class MovieLister... public MovieLister(MovieFinder finder){ this.finder = finder; } class ColonMovieFinder... public ColonMovieFinder(String filename) { this.filename = filename; } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.1 Constructor Injection Am Beispiel: PicoContainer (Konfigurieren) private MutablePicoContainer configureContainer() { //Erzeugung eines Pico Containers MutablePicoContainer pico = new DefaultPicoContainer(); Parameter[] finderParams = {new ConstantParameter ("movies1.txt")}; pico.registerComponentImplementation (MovieFinder.class, ColonMovieFinder.class, finderParams); Registriert eine Klasse(2) unter einem bestimmten Key(1), Der Parameter(3) wird dann mit zum Konstruktor übergeben (später) pico.registerComponentImplementation(MovieLister.class); Registriert eine Klasse(1) bei der von Bedarf ein Objekt erzeugt wird, mit sich selbst(1) als Key return pico; } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.1 Constructor Injection Am Beispiel: PicoContainer (Testen) public void testWithPico() { MutablePicoContainer pico = configureContainer(); siehe oben MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); Holt sich eine Componente die unter diesem Schlüssel reg. wurde Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.2 Setter Injection Am Beispiel: Spring (Komponenten definieren) class MovieLister... private MovieFinder finder; public void setFinder(MovieFinder finder) { this.finder = finder; } class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.2 Setter Injection Am Beispiel: Spring (Konfigurieren) <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> 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.2 Setter Injection Am Beispiel: Spring (Testen) public void testWithSpring() throws Exception { ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml"); MovieLister lister = (MovieLister) ctx.getBean("MovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 3.3 Interface Injection Für alle set-Methoden (Setter-Injection) werden eigene Interfaces definiert. Diese enthalten die Inject-Methoden: public interface InjectFinder { void injectFinder(MovieFinder finder); } class MovieLister implements InjectFinder{ public void injectFinder(MovieFinder finder) { this.finder = finder; } } Konfiguration hat zwei Ebenen: Die Registrierung der Komponenten Die Registrierung der Injektoren 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 4. Service Locator Die Grundidee hinter einem Service Locator besteht in einem Objekt, dass alle Dienste beschafft, die die Applikation benötigen könnte. 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; 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 4. Service Locator Konfiguration und Test class Tester... private void configure() { ServiceLocator.load(new ServiceLocator(new ColonMovieFinder("movies1.txt"))); } public void testSimple() { configure(); MovieLister lister = new MovieLister(); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); } 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 4.1 Nutzung eines abgetrennten Interfaces Eins der Probleme des einfachen 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 nur einen Teil der Schnittstelle ansprechen, ohne die komplette ServiceLocator Schnittstelle zu nutzen. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 4.2 Dynamischer Service Locator Das vorherige Beispiel war statisch, da die ServiceLocator Klasse Methoden für jeden Dienst besaß, den sie benötigte. Das ist aber nicht der einzige Weg. Man kann auch einen dynamischen ServiceLocator 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. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 4.3 Nutzung von Locator und Injection Dependency Injection und Service Locator sind keine sich gegenseitig ausschließende Konzepte. Beispiel: public class MyMovieLister implements MovieLister, Serviceable { private MovieFinder finder; public void service(ServiceManager manager) { finder = (MovieFinder)manager.lookup("finder"); } } Die Service Methode ist ein Beispiel für Interface Injection, die dem Kontainer erlaubt einen ServiceManager in MyMovieLister zu injezieren. 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. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 5. Service Locator vs Dependency Injection - Beide Implementationen stellen das fundamentale Entkoppeln bereit, - Bei beiden ist der Applikations Code 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: - ss gibt keine explizite Anfrage, der Service erscheint in der Applikations Klasse durch Inversion of Control - schwieriger zu debuggen - man sieht die Abhängigkeiten einfacher (Im Konstrukor oder bei den Set-Methoden) 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 6. Constructor vs Setter Injection - Beide können sehr einfach Verbindungen zwischen Komponenten herstellen Constructor: - 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 - Konstruktoren werden nicht automatisch vererbt 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler IoC Containers and the Dependency Injection pattern University of Applied Sciences 7. Fazit 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 gewissen Problemen kommt. 11.12.2006 | Komponentenbasierte Softwareentwicklung | Dependency Injection | André Kley, Julian Päuler