BLOG

Jueves, 07 Diciembre 2017 08:14

¿Qué es la inyección de dependencias, para qué sirve y qué significan los tiempos de vida en su implementación?

En esta ocasión presentamos a Viacheslav Guevara, Developer en Trans Solutions Systems, quien nos explicará a detalle uno de los cinco principios S.O.L.I.D. La inyección de dependencias ayuda a los programadores en la solución de problemas diarios. Veremos sus ventajas y con un sencillo ejemplo aprenderemos a implementar este principio. 

La Inyección de Dependencias es un concepto que hoy en día se ha convertido en uno de los principios más usados en el mundo de desarrollo de aplicaciones. Es igual de importante para el desarrollo de componentes back-end (donde se implementó inicialmente), como de front-end. Angular JS, uno de los frameworks de JavaScript más usados en estos últimos años, y basa su funcionamiento en este principio. Es por ello que, actualmente, se ha convertido en un requisito indispensable para todo programador entender este patrón y saber cómo implementarlo según sus necesidades.

 

¿Qué es la inyección de dependencias y para qué sirve?

La inyección de dependencias (DI, por sus siglas en inglés) es un patrón usado en el diseño orientado a objetos de una aplicación. Es parte de uno de los cinco principios de diseño de clases conocido como S.O.L.I.D.

Como todo patrón de diseño, DI tiene como finalidad solucionar un problema común que los programadores encuentran en la construcción de aplicaciones. Este es, mantener los componentes o capas de una aplicación lo más desacopladas posible. Busca que sea mucho más sencillo reemplazar la implementación de un componente por otro. Así, evitar un gran cambio o impacto en la aplicación que pudiera originar que deje de funcionar por completo.

Para cumplir con dicho objetivo, DI nos permite inyectar comportamientos a componentes haciendo que nuestras piezas de software sean independientes y se comuniquen únicamente a través de una interface. Esto extrae responsabilidades a un componente para delegarlas en otro, estableciendo un mecanismo a través del cual el nuevo componente puede ser cambiado en tiempo de ejecución. Para lograr esta tarea DI se basa en un patrón más genérico llamado Inversión de Control (Inversion of Control).

Es necesario realizar ciertas modificaciones en el código fuente. Por ejemplo: el uso de interfaces que expongan la firmas de los métodos y operaciones de los que es responsable un componente, la eliminación de la instanciación de objetos o la necesidad de un modo de configuración que indique qué clases se instanciarán en el caso de solicitarlo.

Al igual que cualquier técnica o herramienta, DI tiene sus ventajas e inconvenientes. Un uso indebido, pensando que nos va a solucionar nuestros problemas, se puede volver en nuestra contra creando una arquitectura compleja e innecesaria.

Un ejemplo usando Autofac como IoC…

Vamos a implementar un pequeño ejemplo sobre DI, usando Autofac como IoC container. Autofac es un componente en el que ASP.Net Core se basa para implementar el DI en aplicaciones construidas con dicha versión de framework. Es recomendable entender cómo funciona e ir obteniendo experiencia en su uso si se tiene planeado construir aplicaciones en un futuro. (http://autofaccn.readthedocs.io/en/latest/index.html)

Para este ejemplo, vamos a implementar un componente “multiplicador” que va simplemente a realizar la multiplicación de 2 números enteros. Comencemos:

Utilizaremos como IDE Visual Studio. Iniciamos este y creamos 2 proyectos en una solución a la que llamaremos “Demo.InyeccionDependencia”. Uno será de tipo “Console App” y el otro “Class library”.

 

El proyecto de tipo consola tendrá como nombre “Demo.InyeccionDependencia.Applicacion” y el de tipo librería de clases se llamará “Demo.InyeccionDependencia.Implementacion1”.

En este caso, el proyecto de “Demo.InyeccionDependencia.Implementacion1” tendrá la clase con la responsabilidad de realizar la multiplicación de 2 números y será utilizada en el proyecto “Demo.InyeccionDependencia.Applicacion”. Así, separaremos la aplicación en 2 componentes: uno que multiplica y otro que consume o invoca dicha funcionalidad. 

 

Comencemos implementado una clase en “Demo.InyeccionDependencia.Implementacion1”. Esta se llamará “Multiplicador”. Expondrá un método para multiplicar números, la cual recibirá 2 números enteros y retornará el producto de usando la operación de multiplicación. 

 

En “Demo.InyeccionDependencia.Applicacion” agregaremos una referencia al proyecto “Demo.InyeccionDependencia.Implementacion1” para poder hacer uso de la clase “Multiplicador” creada anteriormente.

Implementaremos en la clase “Program” en método “Main”, lógica para solicitar a un usuario que ingrese el multiplicando y el multiplicador para proceder a retornar el producto de ambos usando el método “MultiplicarNumeros” de la clase “Multiplicador”. 

 

Si probamos la aplicación consola veremos que la lógica de multiplicación se realiza correctamente. 

 

Hasta este punto, aún no hemos implementado la Inyección de Dependencias. Nótese que la clase “Program” que inicia la aplicación, conoce de la existencia de la clase “Multiplicador” y que debe ser instanciada para acceder al método “MultiplicarNumeros” por lo que ambos componentes en este caso estan fuertemente acoplados.

Si quisieramos cambiar la clase “Multiplicar” por otra que realice la misma acción tendriamos que modificar la lógica de la clase “Program”. Para este caso sería rápido porque es el punto donde la clase “Multiplicar” es utilizada.

Una aplicación más compleja, cuenta con muchas capas. Donde una clase se instancia y se invocan sus métodos en cientos de otras clases. Realizar la modificación en cada una de ellas para ejecutar el reemplazo de dicho componente seria todo un dolor de cabeza. Para ello implementar la Inyección de Dependencias nos facilitará el reemplazo de componentes.

Ahora procederemos a realizar los cambios necesarios sobre esta solución para implementar la inyección de dependencias.

Primero crearemos un nuevo proyecto: “Demo.InyeccionDependencia.Abstracciones”. Este abstraerá los métodos a usar de la clase “Multiplicador”. Este será también de tipo “Class library” y crearemos una interface llamada “IMultiplicador” con la abstracción o firma del método “Multiplicar”.

 

Procederemos a modificar la clase “Multiplicador” de tal manera que implemente la interface “IMultiplicador” creada anteriormente. Agregaremos una referencia al proyecto “Demo.InyeccionDependencia.Abstracciones”, y adicionaremos 2 líneas de código para mostrar en la pantalla el nombre del componente usado para realizar la multiplicación. Por último, renombraremos la clase “Multiplicador” por “Multiplicador1”. 

 

A continuación, procederemos a crear un nuevo proyecto de clases llamada “Demo.InyeccionDependencia.Implementacion2”. Crearemos una clase con el nombre “Multiplicador2” el cual implementara la interface “IMultiplicador” para lo que agregaremos una referencia al proyecto “Demo.InyeccionDependencia.Abstracciones”. También implementaremos el método “MultiplicarNumeros”, pero en este caso usando un algoritmo de sumas sucesivas y también indicando el componente usado como en la clase “Multiplicador1”. 

 

Para verificar que ambas clases, “Multiplicador1” y “Multiplicador2”, funcionen correctamente y retornen el mismo resultado, en la clase “Program” procederemos a agregar las referencias a cada proyecto de cada clase y reemplazaremos la línea de código donde se instancia y se invoca el método “MultiplicarNumeros”.

Clase Multiplicador1: 

 

 Clase Multiplicador2

 

 

Hemos verificado que ambas clases funcionan y retornan el mismo resultado. Para terminar, procederemos con la implementación de Inyección de Dependencias adicionando el componente de IoC de “Autofac” para realizar el cambio de intercambio de los componentes “Multiplicador1” y “Multiplicador2” entre sí.

En el proyecto “Demo.InyeccionDependencia.Aplicacion” agregaremos el paquete nuget “Autofac”.

 

A continuación, crearemos una clase estática llamada “ConfiguracionIoC”. En ella realizaremos las configuraciones de inyección de dependencias siguiendo la documentación de “Autofac” (http://autofaccn.readthedocs.io/en/latest/index.html).

Luego, instauraremos un método llamado “GenerarContainer” que retornará el container de IoC que realizará la inyección de componentes en la aplicación y en ella registraremos a la clase “Multiplicador1” como instancia por defecto de la interface “IMultiplicador”, esto es, cada vez que en la aplicación se requiera una implementación de la interface “IMultiplicador” en container retornara una instancia de clase “Multiplicado1”. Para ello, agregaremos en la clase “ConfiguracionIoC” una referencia a los proyectos “Demo.InyeccionDependencia.Abstracciones”, “Demo.InyeccionDependencia.Implementacion1” y a la librería “Autofac”. 

 

Con la configuración del container de IoC realizada, procederemos a modificar la clase “Progam”. En ella agregaremos líneas de código para obtener el container de IoC para realizar la inyección de componentes y reemplazaremos la forma en que se instancia las clases “Multiplicador1” y/o “Multiplicador2”. Nótese que ahora la clase “Program” ahora solo tiene las referencias a la librería “Autofac” y al proyecto “Demo.InyeccionDependencia.Abstracciones”. 

 

Ahora ejecutamos nuestra aplicación consola. Podemos verificar que el container IoC realiza la inyección del componente “Multiplicador1” correctamente. 

 

Para concluir, procederemos a realizar el reemplazo del componente “Multiplicador1” por “Multiplicador2”. Para ello, en la clase “ConfiguracionIoC” agregaremos la referencia al proyecto “Demo.InyeccionDependencia.Implementacion2” y modificaremos la configuración del container IoC generado para que inyecte ahora el componente “Multiplicador2” en vez de “Multiplicador1”  para la interface “IMultiplicador”.  

 

Procederemos a ejecutar nuestra aplicación nuevamente y verificamos que la inyección del componente “Multiplicador2” se efectúe correctamente. 

 

Como podemos apreciar en esta última parte, el cambio de componentes para realizar la multiplicación de números del componente “Multiplicador1” por el “Multiplicador2” no requirió hacer alguna modificación en la clase “Program” donde se invoca la función “MultiplicarNumeros”. Sólo se tuvo que modificar la clase que efectúa la configuración del container IoC que usa la aplicación. En una aplicación más compleja si se implementara el patrón de “Inyección de Dependencias” sólo sería necesario realizar las modificaciones en la clase o archivo que realiza la configuración del container IoC.

Con este ejemplo, hemos podido apreciar cómo el patrón de “Inyección de Dependencias” nos ha ayudado a desacoplar los componentes usados por una aplicación. Si se desea realizar el reemplazo de alguno de ellos por otros mejores o más óptimos será mucho más sencillo. No requerirá de muchos cambios en la aplicación facilitando su mantenimiento y el testeo de sus componentes.