Dependency Injection
and Service Locator
both performs wiring of application.
Service
= Component
Component
should be used locally.Service
will be remotely accessed/used.Registry
Inversion of Control
What is Control
Inversion of Control
means providing any kind of callback (via implements or controls reaction) instead of acting on it directly
Dependency Injection
Dependency Injection
is a specific version of IoC
that is focused on removing dependency from your code.
Dependency Injection
depends upon abstractions
and not depend upon concretions
via passing/injecting concrete implementation to the abstraction as a parameter.
In below example, the TextEditor
class is directly dependent upon SpellChecker
because it is instantiated inside the class definition.
public class TextEditor { private SpellChecker checker; public TextEditor() { this.checker = new SpellChecker(); } }
Applying the inversion, we are creating abstraction by passing the SpellChecker
in the TextEditor
's constructor
signature.
public class TextEditor { private IocSpellChecker checker; public TextEditor(IocSpellChecker checker) { this.checker = checker; } }
Passing the actual implementation of SpellChecker
would look like this:
SpellChecker sc = new SpellChecker(); // dependency TextEditor textEditor = new TextEditor(sc);
This allows TextEditor
class to choose which SpellChecker
implementation to use because the dependency is injected.
also see "service-locator" page
// injects the finder class MovieLister... public MovieLister(MovieFinder finder) { this.finder = finder; } // finder class ColonMovieFinder... public ColonMovieFinder(String filename) { this.filename = filename; } // injection container private MutablePicoContainer configureContainer() { MutablePicoContainer pico = new DefaultPicoContainer(); Parameter[] finderParams = {new ConstantParameter("movies1.txt")}; // register both containers to global pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams); pico.registerComponentImplementation(MovieLister.class); return pico; } // test public void testWithPico() { // mock container MutablePicoContainer pico = configureContainer(); // calling the lister MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); // calling the method on the service Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); // test assertion assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }
// lister to accept injection by defining a setter method class MovieLister... private MovieFinder finder; public void setFinder(MovieFinder finder) { this.finder = finder; } // setter for filename class ColonMovieFinder... public void setFilename(String filename) { this.filename = filename; } // configuration <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> // test 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()); }
// finder interface public interface InjectFinder { void injectFinder(MovieFinder finder); } // must be implemented by class that wants to use a finder (lister) class MovieLister implements InjectFinder { public void injectFinder(MovieFinder finder) { this.finder = finder; } } // injecting filename to finder implementation public interface InjectFinderFilename { void injectFilename (String filename); } class ColonMovieFinder implements MovieFinder, InjectFinderFilename... public void injectFilename(String filename) { this.filename = filename; } // configuration class Tester... private Container container; private void configureContainer() { container = new Container(); registerComponents(); registerInjectors(); container.start(); } // container registration class Tester... private void registerComponents() { container.registerComponent("MovieLister", MovieLister.class); container.registerComponent("MovieFinder", ColonMovieFinder.class); } // injector registration class Tester... private void registerInjectors() { container.registerInjector(InjectFinder.class, container.lookup("MovieFinder")); container.registerInjector(InjectFinderFilename.class, new FinderFilenameInjector()); } public interface Injector { public void inject(Object target); } // component implementing the injector itself (should we do this?) class ColonMovieFinder implements Injector... public void inject(Object target) { ((InjectFinder) target).injectFinder(this); } class Tester... public static class FinderFilenameInjector implements Injector { public void inject(Object target) { ((InjectFinderFilename)target).injectFilename("movies1.txt"); } } // corresponding test class Tester… public void testIface() { configureContainer(); MovieLister lister = (MovieLister)container.lookup("MovieLister"); Movie[] movies = lister.moviesDirectedBy("Sergio Leone"); assertEquals("Once Upon a Time in the West", movies[0].getTitle()); }