¿Qué es la inyección de dependencias?

Desde hace unos años, con la aparición del framework Spring, se habla mucho del patrón Inyección del Dependencias. Al igual que cualquier técnica o herramienta tiene sus ventajas e inconvenientes ya que un uso indebido, pensando que nos va a solucionar nuestros problemas, se puede volver en nuestra contra creando una arquitectura compleja e innecesaria.

El patrón de inyección de dependencias consiste en hacer que nuestras piezas de software sean independientes comunicándose únicamente a través de un interface. Esto implica muchas modificaciones en el código fuente como el uso de implementaciones, la eliminación de la instanciación de objetos mediante la instrucción new o la necesidad de un modo de configuración que indique que clases se instanciarán en el caso de solicitarlo.

Introducción

Haciendo un simil como un PC clónico, gran parte de elementos como discos duros, tarjetas de sonido, tarjetas gráficas son independientes. Esto significa que uno puede cambiar la tarjeta de sonido por otra marca y mientras que cumpla una interfaz y unas normas de diseño, esta funcionará.

El modo de funcionamiento es como una caja negra para el propio ordenador. Este solo se preocupa de utilizar las funciones básicas de los dispositivos y mediante un driver adicional configurar las características especiales.

En el software ocurre lo mismo. Es posible crear una aplicación sin que hayan partes concretas desarrolladas mediante el uso de interfaces pero con una complejidad que es que estas piezas no suelen tener un estándar a seguir, es decir, es el programador el que decide que funciones y parámetros tendrá el interface.

Esto nos lleva a una segunda pregunta o reflexión, si es este programador el que hace todo y nadie más va a añadir estas piezas de manera que haya que seguir un estándar ¿realmente es necesario la inyección de dependencias?. La inyección de dependencias te dan más ventajas que me gustaría repasar aunque desde mi punto de vista no llega a ser una bala de plata.

Eliminación de instrucciones new

La primera consecuencia a tener en cuenta en tu código es que la instancias inyectadas no se generarán desde tu código fuente si no que se harán por reflexión o mediante algún framework.

Esto significa que tu siempre tratarás con un interfaz y para utilizar una instancia de una clase deberás invocar al repositorio de dependencias que se encargará de gestionar que objeto se desea instanciar y generarlo.

Sustitución de piezas software

Como he comentado anteriormente, una gran ventaja es la posibilidad de modificar una pieza de software por otra sin necesidad de reprogramar las clases que lo van a utilizar. Volviendo al símil del PC, no es preciso volver a construir otro PC para cambiar la tarjeta de sonido.

Esto puede ser interesante en desarrollos en los que es posible añadir o quitar elementos variables como plugins. Los IDEs, navegadores, editores de sonido, editores gráficos y actualmente muchas más aplicaciones, permite configurar la parte de efectos o funcionalidades mediante plugins dejando únicamente los ficheros en un directorio.

Diseñar una aplicación para que sea extensible por otros programadores es muy interesante cuando esta aplicación es de uso general. Para aplicaciones a medida quizá lo vea menos práctico dependiendo también del tamaño de la aplicación.

En lineas generales, si tu aplicación puede ser extensible, si que es interesante utilizar inyección. En caso contrario, me replantearia si realmente necesitas poner un framework sobre esto.

Mejora tus test de aplicaciones

La no utilización de la instrucción new, tiene una gran ventaja para el testeo automático de aplicaciones que consiste en hacer mocking. Esta técnica consiste en simular piezas de software dependientes y reemplazarlas por otras más simples y más rápidas para ejecutar multitud de test.

Por ejemplo, si un test de unidad tuviese una dependencias a un código para que conecte a la base de datos ese test se puede llegar a ejecutar tremendamente lento. Si tenemos una base de datos con mucho volumen o tenemos muchos test a ejecutar, puede que llegue a ser inviable realizar test de unidad. Como la inyección de dependencias nos permite modificar estos componentes, podemos simular algo más eficiente.

Perdida de la orientación de la arquitectura en tiempo de diseño

Una gran ventaja de ciertos IDEs en la actualidad es de poder acceder rápidamente al código fuente de las clases y en modificar el código mientras estás depurando. Al usar inyección de dependencias se pierden estas características.

Por una parte, al trabajar con interfaces y no con la clase directamente, no puedes saber en tiempo de diseño que estás ejecutando así que se pierde la visión de todo el código fuente para encontrar el error.

Es muy importante ser muy ordenado con los nombres y las carpetas donde dejas las clases para conocer que es lo que se está ejecutando y encontrarlo navegando por los directorios. Sin inyección de dependencias, poniendo directamente la clase en el código y pulsando una tecla de ir al código fuente se abre una pestaña nueva con ese fichero para editarlo. Algo muy sencillo pero que ahorra muchísimo tiempo.

Depuración de clases ya compiladas

Aunque conseguimos grandes ventajas en el desarrollo de test como el uso del mocking, muchas veces el problema en los test es preveer lo que no conocemos. Carácteres ocultos, datos incorrectos, valores extraños y un montón de información corrupta puede hacer que nuestro sistema no funcione bien y no lo hayamos previsto. En ese caso los test automáticos no funcionarán y tendremos que depurar el código para encontrar ese misterioso motivo.

Al depurar bajo las condiciones en las que se produce el fallo, finalmente nos encontremos con el problema. Si todo está dentro del mismo proyecto, no habrá problemas de modificarlo pero si estuviese en una clase ya compilada puede que el IDE nos indique que ese código no puede abrirse o modificarse estando el programador obligado a recompilar el proyecto con el objeto dañado.

Esto no es algo propio del patrón de inyección de dependencias pero sí que el patrón empuja o induce a crear un diseño modular y con componentes independientes y como consecuencia a crear diferentes proyectos para cada módulo.

Configuración y más configuración

La configuración también es una tarea a añadir en el caso de utilizar inyección de dependencias. Al hacer la inyección se traslada la responsabilidad de la instanciación de un código imperativo a un código declarativo.

Esta declaración puede ser de muchas formas como xml, anotaciones, ficheros de texto, almacenándola en la base de datos, etc. Dependerá del framework o de nuestro propio algoritmo de inyección el que se use una forma u otra. Poco a poco se trabajan en estándares para normalizar el modo de inyección como en Java a través del JSR 330.

Sin embargo, desde mi punto de vista, aún hay mucho que investigar y mucho que crear ya que si el principal objetivo de la inyección de dependencias es la sustitución de piezas software de manera modular y reutilizables, habría que trabajar en una especificación multilenguaje.

Desde mi punto de vista, centralizar toda la configuración en un único xml es un problema ya que aunque no se modifique el código fuente, siempre hay un sitio centralizado que hay que modificar y en el caso de provocar aquí un fallo la aplicación entera dejará de funcionar. En el caso web, espero que en un futuro cambie con versiones de servidores como Tomcat 7 donde cada módulo puede tener su propio xml de configuración, no haciendo modificar el proyecto base (ni su xml) por la incorporación de un módulo.

Conclusión

Con inyección de dependencias perdemos visión (no abstracta) en tiempo de diseño y con ello el datalle de lo que hace la aplicación. Además perdemos funcionalidades de depuración en módulos en proyectos externos ya que es preciso recompilar estos para poder modificarlos. Sin embargo, ganamos en la posibilidad de hacer mocking para el testeo automático y ganamos en la posibilidad de reemplazar piezas que cumplan un estandar.

La siguiente cuestión que habrá que plantearse, ¿se pueden definir estándares para establecer unos interfaces según el negocio y abrir un mercado de desarrollo de módulos?. Para que algo se convierta en estándar es preciso que sea muy utilizado.

Ahora mismo estamos en estado de experimentación aún definiendo como se tiene que hacer la inyección. Cuando ya sepamos como se debe hacer la inyección pasaremos a la siguiente etapa que es la definición de interfaces estándares.

Portada de Genbeta