En el artículo de introducción a los servicios, ya empecemos a ver que cuando creamos un desarrollo de una web, las clases, suelen requerir de otras clases.

Analizando a detalle Tight Coupling vs Dependency Injection

¿Cómo podemos trabajar desde una clase con los objetos de otras clases? Y vimos la primera manera la tradicional de la programación, utilicemos el conocido como Tight Coupling.

Tight Coupling consiste en realizar instanciaciones de las clases  que vamos a utilizar con el fin de instancia/crear (mediante a sus constructores) objetos de dichas clases dentro del constructor de la clase en cuestión sobre la que queremos utilizar dichos objetos.

Es decir creabamos objetos de las clases que vamos a utilizar dentro de la clase que los quiere utilizar.

Un ejemplo de Tight Coupling, podría ser:

Vemos que tenemos una clase A (AppComponent) instancia es decir crea un objeto serviceB y serviceC del molde de dichas clases BServive y CService. Y por tanto, aunque nuestra aplicación funciona, nuestra clase tiene una dependencia de dichas clases B y C y por tanto, no va a actuar de forma independiente a dichos servicios.

En la actualidad es una aplicación muy muy pequeña, y por tanto, modificar una clase no nos supone un gran esfuerzo. Pero como ya hablemos sobre dicha técnica, cuando nuestra aplicación empieza a escalar y va aumentado su tamaño y las clases de los servicios BService y CService empiezan a ser utilizada cada vez en más sitios. Nos encontramos con el problema que cualquier modificación puede provocarnos tener que modificar 70 u 80 clases por cada una de las clases que modifiquemos.

Esto, multiplicado por una gran cantidad de modificaciones y acompañado de no solo 3 clases sino de muchas, nos generará lo que se conoce como un árbol o relación de dependencias que cada vez será más y más complejo.

Hasta llegar al punto que por ejemplo, la clase A depende de la clase C, que a su vez depende la clase H, que a su vez depende de la clase K.

En muchas ocasiones queremos realizar una nueva implementación (Poner en funcionamiento o llevar a cabo una cosa determinada como un testeo que podamos revertir por ejemplo) basada sobre nuestra actual clase C. Pero queremos mantener la actual clase C, tal cual, es decir no la queremos perder.

Ante el hipotético caso de que tengamos el problema resuelto con Tight Coupling, es decir, llamando en el constructor de todas las clases que dependen del  servicio de la clase C tendremos que modificar una a una todas las instanciaciones del constructor de dicha clase (70-80, etc.) hacía el de la nueva clase C mejorada.

Además si queremos trabajar con la clase C, necesitaremos previamente antes de poder instanciar dicha clase crear todas las clases que necesita de dicha clase para poder funcionar.

Todos estos problemas, son los que nos va a ayudar a resolver la inyección de dependencias.

  • Substituir un objeto por otro de una forma sencilla siempre y que ambos contengan la misma interfaz.
  • Instanciación inmediata de objetos que dependen/requieren de otros objetos sea inmediata, sin tener que instanciar previamente a todos los objetos dependientes.

¿Qué es la inyección de dependencias?

La inyección de dependencias o dependency injection, es un patrón que consiste en inyectar los objetos (es decir, las instancias de nuestras clases de las que dependemos) en nuestras clases a través de los parámetros del constructor de nuestro componente.

Angular proporciona mecanismos para que la inyección de dependencias sea sencilla y se orienta en los objetos.

Entonces ¿Cómo solucionamos dicho problema mediante a la inyección de dependencias?

Creando los objetos dependientes desde la zona de parámetros del constructor de la clase y no desde la clase o desde el interior del propio constructor.

El principal beneficio de utilizar la inyección de dependencias es que para trabajar con la clase C no necesitamos saber que necesitamos la clase J, K, I, H y G. Ni tener que crear G e H, para poder trabajar con I y posteriormente crear H para poder trabajar con G y finalmente poder crear nuestra clase C.

Ya que este patrón (el dependency injection) nos va a permitir realizar la instanciación de cualquier objeto cuando el programador lo solicite sin tener la necesidad de que el programador sepa cada una de las clases sobre las que necesitamos crear objetos y que nuestro objeto solicitado requiere para ser creado.

Este proceso de crear un objeto sin tener que preocuparnos de conocer todas y cada una de las dependencias de objetos que nuestra clase va a tener suele recibir el nombre de contenedor de servicios (o inyector)

El contenedor de servicios (o inyector) es capaz de crear objetos que dependen de otros. Sin necesidad alguna de saber cómo se crean dichos objetos.

Maneras de inyectar un servicio/clase/componente

Existen distintas alternativas sobre cómo podemos registrar un servicio para poder posteriormente inyectar dicho servicio en el constructor de un componente con el fin de poder consumirlo desde el constructor de la clase que anteriormente era dependiente de dicho servicio de Angular.

Dichas alternativas se conocen como provider scope y son:

  • Inyectar en los servicios @Injectable: (disponible a partir de la versión 6 de Angular) será visible desde todos los componentes de nuestra aplicación.
  • Hacer uso de providers a nivel del modulo: el servicio será visible exclusivamente dentro de las clases (componentes, pipes, etc) de nuestro modulo.
  • Hacer uso de providers a nivel del componente: únicamente será visible para  dicho componente de nuestra aplicación.

Inyectar en los servicios @Injectable

Para inyectar un servicio, escribimos el acceso a la clase en nuestro caso, solo será visible a nivel de la clase actual ya que usamos private, junto al nombre con el que trabajaremos en dicho constructor acompañado de dos puntos : y la clase que instanciaremos para crear el objeto.

De hecho este caso en particular nos funciona debido a que el elemento que nos va a permitir en este caso de inyectar dicho servicios en el constructor de nuestro componente app.component.ts es el decorador @Injectable({ provideIn: ‘root’ });

Muestra de ello, es que si eliminamos dicho decorador del nuestros servicios:

Y volvemos a la página de nuestra aplicación, nos mostrará un error tal que así:

Corregir esto tiene varias soluciones, vamos a verlo.

Hacer uso de providers a nivel del modulo

Otra opción para trabajar con dichos servicios es el uso de providers dentro del módulo en el que hacemos uso de dichos servicios.

En dicho array de providers añadiremos el nombre de las clases que contienen dichos servicios y lo cual nos permitirá añadir los servicios para poder inyectarlos en nuestro constructor del componente para poder trabajar con ellos.

Hacer uso de providers a nivel del componente

O como alternativa realizar lo mismo que hemos realizado anteriormente en el providers del módulo a nivel de

Ejemplo de servicio de logger sobre componentes

Vamos a hacer una aplicación que nos permite seleccionar una bebida y una comida como si de un restaurante se tratará y queremos que cada vez que el usuario selecciona un producto, genere un log. En dicho log, mostraremos el valor junto a la fecha y la hora.

Para ello, partimos de una estructura inicial de un proyecto de Angular por lo que no tendremos servicios ni componentes adicionales a los que trae la estructura de angular por defecto.

Primeramente, vamos a modularizar nuestra aplicación generando dos módulos:

Dentro de cada uno de los módulos, vamos a generar un componente y se lo vamos a signar al módulo que corresponda mediante a –module=nombreDelModulo. Vamos a verlo:

Para acceder a los componentes de nuestros nuevos dos módulos que acabamos de crear dentro de nuestra aplicación sean visibles dentro de nuestra aplicación, tenemos que exportarlos:

Una vez realizado esto os aconsejo reiniciar el proyecto nuevamente ya que nuestro proyecto tiene que importar unos componentes nuevos. Y aquí podemos ver el resultado:

Bien, una vez hemos creado y comunicado a los diferentes módulos de nuestra aplicación, vamos a crear nuestro servicio de logger:

Como vamos a utilizar [(ngModel)] dentro de nuestro template de HTML añadimos FormsModule al modulo bebida.module.ts:

Una vez creados los componentes y el servicio a injectar, vamos crear la selección de la bebida dentro del HTML de dicho componente:

Y repetimos la operación con el de la comida:

Vamos al servicio de logger y:

Y modificamos nuestros templates y en ambos reutilizamos el servicio:

Si ahora vamos a nuestra aplicación, podemos ver que hemos creado un servicio de log que muestra de un color los mensajes de comida y con otro los de comida:


Espero que os haya gustado. Un saludo!