Dependency Injection o DI y Spring
Intro a la DI
Una de las mayores ventajas de las pruebas unitarias, más incluso que la posibilidad de testear las aplicaciones es sin duda el hecho de que desarrollar las pruebas unitarias te lleva de manera irremediable a desarrollar un mejor código: como poco te obliga a crear clases con métodos que hacen una tarea muy concreta. También está la posibilidad de desarrollar partiendo de las pruebas (TDD) con un par.
Muchas veces cuando tratas de aprender algo una cosa lleva a la otra. Si tratas de profundizar en frameworks como mockito para falsear objetos en los tests unitarios (mock objects) cuando buscas ejemplos te empiezas a encontrar que necesitas ir más allá en el desacoplamiento de clases y aparece la DI (Dependency Injection). Y te encontrarás con el clásico artículo de Fowler
http://martinfowler.com/articles/injection.html que bueno en fin, es un gurú pero digamos que no es para todos los públicos. Fowler pone como ejemplo el framework Spring, una herramienta que ahora hace mil cosas pero cuya función primigenia es facilitar la inyección de dependencias.
Los orígenes de Spring se introducen por Rod Johnson en el libro Expert One-to-One: J2EE Design and Development, en especial en el capítulo 11 si mal no recuerdo. Ahí habla de los BeanFactories, el Application Context y de ficheros XML con los que generamos instancias y asignamos propiedades.
Bueno, yo lo que sugiero es echar un ojo a este ejemplo que es más simple:
/**
* Representa a un mensajero de una empresa
* de mensajería.
* @author Pello Altadill
* @greetz Taladros Friend's
*/
public class Mensajero {
private Furgoneta vehiculo = new Furgoneta();
public Mensajero () {
}
public void desplazarse () {
vehiculo.moverse();
}
}
El mensajero necesita obviamente un vehículo, y por tanto tiene un atributo que él mismo
se encarga de instanciar. Hay un problema claro, y es que el Mensajero se limita a
desplazarse con una furgoneta y no podrá usar ningún otro vehículo. Encima no podremos
testear que realmente se llama al método moverse() del atributo vehículo que es privado.
A pesar de todo necesitamos que las clases hablen cooperen entre ellas pero debemos procurar
no acoplarlas tanto.
Para conseguir ese equilibrio usamos la DI que básicamente consiste en que esas dependencias se asignarán
desde fuera de la clase Mensajero. Hay dos formas básicas de hacerlo, a través de un parámetro en el constructor
o a través de métodos set. Además no vamos a asignar una clase concreta sino un interfaz lo cual nos va a facilitar
dos cosas: poder meter distintos tipos de vehículo (que deben implementar ese interfaz) y simplificar
el testeo del método desplazarse.
Este sería el interfaz Vehículo:
/**
* Interfaz para vehículos
* @author Pello Altadill
* @greetz Central
*/
public interface Vehiculo {
public void moverse();
}
Y esta una implementación de ese interfaz:
/**
* Implementamos un vehículo con la clase furgoneta.
* @author Pello Altadill
* @greetz Tejedor
*/
public class Furgoneta implements Vehiculo {
private int deposito;
public void moverse() {
if (deposito > 0)
deposito--;
}
}
Ahora cambiamos al mensajero por esto:
/**
* Representa a un mensajero de una empresa
* de mensajería. Version desacoplada
* @author Pello Altadill
* @greetz Bar Taladros crew
*/
public class Mensajero {
private Vehiculo vehiculo;
public Mensajero (Vehiculo vehiculo) {
this.vehiculo = vehiculo;
}
public void desplazarse () {
vehiculo.moverse();
}
}
Ok, es muy simple como se puede apreciar. El vehículo se asignara desde fuera. Y como se puede ver hasta ahora
no hemos creado clases complejas, todo tiende a POJOs, (Plain Old Java Objects). Con Spring, usando un fichero XML
podremos instanciar Vehículo (un objeto de la clase Furgoneta) e inyectarla en una nueva instancia de Mensajero.
Aparte de conseguir desacoplar las clases, facilitaremos que se pueda testear el método desplazarse, ya que ahora
tenemos acceso al Vehículo que se utiliza, es un interfaz y podemos mockearlo o falsearlo en el test. Si lo que queremos
es comprobar que realmente se llama al método moverse() hariamos algo así:
import static org.mockito.Mockito.*;
import org.junit.Test;
/**
* Testeando el Método desplazarse de Mensajero
* @author Pello Altadill
* @greetz UUuuuOOooo
*/
public class MensajeroTest {
@Test
public void desplazarseLlamaAMoverse () {
// Creamos el mock para las pruebas
Vehiculo vehiculoFalso = mock(Vehiculo.class);
// Inyectamos esa instancia para la prueba
Mensajero mensajero = new Mensajero(vehiculoFalso);
mensajero.desplazarse();
// AHORA SÍ, podemos verificar si se llama a moverse una vez
verify(vehiculoFalso, times(1)).moverse();
}
}
Bueno, "Talk!, Talk is for lovers Merlin!! I need a Spring sample to be King!!"
Esto, ejem.. quiero decir que este post pretendía introducir de alguna forma la DI o inyección de dependencia
y tratar de vender los muchos beneficios de Spring que así a botepronto se me ocurren tres:
- Desacoplar las clases y facilita que todo sean POJOs y te centres en el negocio
- Facilitar los tests unitarios
- Permite introducir los aspectos (AOP), cosa que es crema máxima. Otro día.