Dependency Injection
Veamos un ejemplo para entender de que estamos hablando.
Estamos escribiendo un componente que nos devuelve la lista de películas dirigidas por un director en particular.
La implementación del método es extremadamente ingenua pero nos sirve para nuestro fin.
Lo importante de este método es el finder ya que nuestro algoritmo es independiente de como se persisten las películas.
class MovieLister...
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}
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()]);
}
En este caso, en el constructor de MovieLister estamos instanciado una implementación de MovieFinder que podría levantar un archivo delimitado.
La interface MovieFinder es bien simple:
public interface MovieFinder {
List findAll();
}Lo que tenemos hasta acá lo podemos ver representado en el siguiente diagrama, MovieLister conoce tanto a la interface MovieFinder como a la implementación MovieFinderImpl.
La IoC en este caso está dada por "tercerizar" la obtención de la implementación real de la interface. De esta manera el componente MovieLister se independiza de las posibles implementaciones del MovieFinder. Esto lo vemos en el siguiente diagrama
La Dependency Injection puede hacerse de tres maneras:
- Constructor Injection: En este caso la clase MovieLister tiene un constructor que recibe un MovieFinder.
- Setter Injection: Aquí la clase MovieLister posee un método setFinder(MovieFinder finder). Este es el caso de Spring.
Aquí viene la mejor parte, como hacemos para que todo esto funcione al unisono:
En el archivo de configuración de spring definimos lo siguiente (también estamos haciendo que ColonMovieFinder reciba mediante spring el nombre del archivo de donde tomar los datos):<beans>
<bean class="spring.MovieLister" id="MovieLister">
<property name="finder">
<ref local="MovieFinder">
</property>
</bean>
<bean class="spring.ColonMovieFinder" id="MovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans> y un ejemplo de uso sería: - Interface Injection: este caso lo veremos en otro momento ya que no es muy utilizado.
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());
}En resumen, lo principal es que se agrega un nuevo administrador, en el cual se definen para cada interface, una implementación y en tiempo de ejecución se le solicitan a él los beans.