Hoy en d铆a es bien sabido que la utilizaci贸n de patrones y buenas pr谩cticas de programaci贸n nos ayudan en la creaci贸n y mantenimiento de nuestro software.

Hay una gran variedad de patrones interesantes, a medida que los vas aplicando descubres que cada uno de ellos te ayuda a cumplir que tu software sea m谩s *-ble (mantenible, extensible, entendible, testeable, etc.)

En este art铆culo me gustar铆a introducirte el patr贸n Inyecci贸n de Dependencias relacionado con una serie de buenas pr谩cticas y patrones que veremos a continuaci贸n, empecemos.

驴Por qu茅 aprender este patr贸n?

Una de las principales razones para las que me gusta aplicarlo, es para reducir el acoplamiento entre las diferentes piezas de mi software.

La pr谩ctica me demuestra que si reduzco el acoplamiento tendr茅 una mejora en mis -ble(recuerda: mantenible, extensible, entendible, testeable, etc.) Pero鈥 驴Por qu茅 quiero cumplir todo esto? Pues entre otras cosas podr茅 hacer mejor software con menor coste que no tenerlos en cuenta. 驴Qu茅 m谩s me da tener menos costes? Bueno parece que si cumplo todo esto reducir茅 mis tiempos en las mismas tareas, menos estr茅s y qui茅n sabe si una vida m谩s feliz.

Es decir, si usar谩 un poco de clickbait, podr铆a cambiar el t铆tulo por C贸mo ser m谩s feliz gracias a la inyecci贸n de dependencias :)

alt

Aun as铆, puede ser que el clickbait no sea suficiente para convencerte, veamos algunas otras ideas, para ello necesito presentarte a la D de SOLID.

驴Los principios qu茅? Los principios SOLID, son unos principios pensados para hacer nuestro software m谩s -ble son un resumen de las buenas pr谩cticas propuestas en el a帽o 2000 por el famoso **Uncle Bob (Robert C. Martin) **y son unos principios b谩sicos del mundo del software que deber铆as conocer (y no solo porqu茅 los pregunten en las entrevistas).

Si quieres profundizar en SOLID, te recomiendo que le eches un vistazo al e-book de Software Crafters:

Inversi贸n de Dependencias

El principio de Inversi贸n de Dependencias m谩s conocido como Dependency Inversion* nos introduce que dados dos m贸dulos relacionados entre si, por ejemplo A y B. Ninguno de ellos dos deber铆a depender del otro, deber铆amos usar una abstracci贸n para esa relaci贸n. Eso permite que A o B no conozcan realmente al otro, simplemente entienden que cumple esa abstracci贸n mencionada anteriormente.

En la mayor铆a de los lenguajes (en casi todos los que se me ocurren) a esa abstracci贸n la llamamos Interfaz y las interfaces en software no son m谩s que contratos que deben ser cumplidos, es decir si A necesita a B, oficialmente diremos que A necesita a alguien que cumpla la interfaz (el contrato) IB (la mayor铆a de las veces si tenemos un m贸dulo llamado B, al contrato le pondremos una I delante para identificarlo como interfaz de B). Y en este caso B cumplir谩 ese contrato, aunque a A le da igual que qui茅n cumpla IB sea **B, C, D *o* Z.**

alt

驴Interesante verdad? Antes de pasar a la inyecci贸n de dependencias me gustar铆a hablarte de un Patr贸n de Dise帽o tambi茅n muy interesante y relacionado.

Inversi贸n de Control

El patr贸n de dise帽o, Inversi贸n de Control conocido como Inversion of Control (IoC). Nos explica que cuando una clase (o componente, pieza de software) depende de otra clase la primera no deber铆a gestionar el ciclo de vida de la segunda, esto deber铆a ser hecho en otro 谩mbito.

En palabras sencillas, viene a decir que si en el constructor de tu clase haces un new B(); parece que puedes hacerlo mejor.

Este otro 谩mbito no suele estar especificado, aunque casi siempre acabaremos llam谩ndolo Contenedor.

Lo que haremos cuando estemos en A y queramos usar B no ser谩 crear una nueva instancia del segundo, le pediremos a este Contenedor (o el elemento correspondiente) que nos provee de B y ser谩 en este en el que caer谩 la responsabilidad de gestionar todo el ciclo de vida de B.

alt

驴Y entonces qu茅 es la Inyecci贸n de Dependencias?

Una vez damos por hecho que nos interesa la Inversi贸n de Dependencias y la Inversi贸n de Control puede ser muy interesante tener una herramienta (o en este caso un patr贸n) que nos ayude a cumplir ambas.

Esto es la Inyecci贸n de Dependencias, un patr贸n que nos da una soluci贸n a las dos pr谩cticas anteriores, pero no tiene por qu茅 ser la 煤nica soluci贸n a esas pr谩cticas, puede haber otras distintas. Esta simplemente nos da soluci贸n a ambas a la vez.

Cuando estamos en A y queremos B, le pedimos a nuestro Contenedor que por favor nos provee a alguien que cumpla el contrato IB y dependiendo del caso este contenedor nos dar谩 B o un equivalente que corresponda. Por ejemplo, si estamos testeando quiz谩 recibimos MockB.

alt

Empezando a trabajar con Inyecci贸n de Dependencias

Despu茅s de toda la teor铆a expuesta, vamos a empezar a trabajar con la Inyecci贸n de Dependencias de forma pr谩ctica, poco a poco.

Imaginemos primero que tenemos un Servicio llamado CartService b谩sicamente es un servicio que nos ayuda a gestionar el carrito de la compra de nuestro software. Pero tenemos la necesidad de usar otro servicio de Insights para saber cuando un usuario a帽ade un producto, lo borra, etc.

alt

Podemos importar este otro servicio de Insights de diferentes modos, una forma seria importar el objeto entero de tal modo que en cualquier otro servicio que importemos Insights tendremos la misma instancia.

Esto ser铆a lo m谩s parecido a un 鈥欌欌橲ingleton鈥欌欌 que hagamos en Frontend.

alt

Y si no queremos que cada vez que se importe Insights sea la misma instancia, lo que podemos hacer es importar la clase e instanciar cada vez que lo necesitemos. Muy bien esto parece algo m谩s de tipo 鈥欌欌橳ransient鈥欌欌.

alt

隆Felicidades! Con esto tendr铆amos una aproximaci贸n muy simple a lo que es Inyecci贸n de Dependencias pero una vez le铆da la teor铆a podemos al menos entender que est谩 pasando.

El problema es que siguen faltando muchos puntos a comentar, las clases tienen mucha responsabilidad, no he hablado de ning煤n contenedor, ni siquiera menciono las abstracciones, etc.

Vamos a ver la Inyecci贸n de Dependencias paso a paso, hasta llegar a lo que considero el escenario ideal en Frontend para inyectar dependencias.

Paso 1: Semi-Inyecci贸n

alt

En este caso ya no importamos Insights directamente, sino que importamos una Factor铆a, esto nos ayuda a abstraernos y no saber (desde CartService) si este primero es Singleton o Transient de hecho nos da igual.

Adem谩s pasamos las dependencias por el constructor de CartService lo que empieza a parecer una Inyecci贸n al uso.

Este primer nivel es un nivel acad茅mico y no tiene por qu茅 ser un patr贸n ideal para usar en proyectos, pero a veces puede ser m谩s que suficiente (tal y como me demostr贸 mi compa帽ero Carlos de Miguel).

Paso 2: Auto-Inyecci贸n

alt

En el siguiente ejemplo acad茅mico, lo que buscamos es que no tengamos que depender siquiera de ninguna factor铆a, y adem谩s utilizamos unos decoradores para ocultar el hecho de que tengamos que pasar las dependencias por el constructor. Se parece un poco a como lo hac铆a Angular 1 y los decoradores se parece a librer铆as m谩s modernas.

De momento sigue siendo Insights qui茅n decide que tipo de inyecci贸n va a realizar, lo cual es bueno porqu茅 no lo hace CartService pero se puede mejorar.

Paso 3: Inyecci贸n con Contenedores

alt

Este ejemplo empieza ya a ser interesante y algo m谩s pr谩ctico, a帽adimos un Contenedor que se encargue de manejar todo lo relacionado con las dependencias del proyecto (o del m贸dulo).

Esto nos ayuda mucho ya que ahora los Servicios son solo responsables de su l贸gica y es este Contenedor el encargado de decidir:

  • Cuando se instancias las dependencias.

  • C贸mo se instancia cada una de ellas (Singleton, Transient)

  • En qu茅 orden se van a registrar.

Hemos llegado al ecuador de los pasos, pero todav铆a no tenemos algo totalmente funcional, sigamos con el paso 4.

Paso 4: Un mundo m谩s realista

alt

Aunque en el ejemplo anterior tenemos algo bastante funcional, es verdad que eso de pasar al constructor un par谩metro y que m谩gicamente aparezca ah铆 sin darle un tipo o un id o algo es bastante autom谩gico (Angular 1 :D).

En este siguiente paso decoramos los par谩metros asignando un ID que van a ser con los que nuestro contenedor identifique la dependencia.

Esto nos da una soluci贸n bastante sencilla y real, pero en otros lenguajes utilizamos Abstracciones para ahorrarnos el ID.

Paso 5: A帽adiendo Abstracciones

alt

Si a帽adimos TypeScript a la mezcla podemos usar Interfaces como Abstracci贸n.

Primero, perm铆teme comentar que esto no es un paso obligatorio si trabajas en Frontend, pero si quieres una capa de Abstracci贸n va a ayudarte mucho.

Ahora identificamos las dependencias por su Interfaz adem谩s de por su ID y aqu铆 es donde se abre un peque帽o debate.

Aunque solo con la Interfaz deber铆a ser m谩s que suficiente, por el momento los navegadores no interpretan TypeScript, as铆 que una vez compilado el navegador no va a tener nada que interpretar si no tenemos ese ID, por suerte si ponemos un poco de la famosa automagia, podemos llegar a ocultar eso con un poco de Reflection.

Paso 6: Implementando DI en los Componentes

alt

Aunque en la mayor铆a de lenguajes de programaci贸n usamos Inyecci贸n de Dependencias a trav茅s del constructor, en los componentes puede ser m谩s sencillo cambiar a inyecci贸n por propiedad, como por ejemplo se hac铆a en algunas versiones de Android.

La funcionalidad es exactamente la misma pero nos quedar谩 un c贸digo m谩s parecido al de la foto anterior, al que tambi茅n se le ha a帽adido Reflection para que no tengamos que usar los id* (aunque debe ser siempre un paso opcional).*

驴Pero hay de verdad algo real detr谩s de todo esto?

La librer铆a para inyectar dependencias

C贸mo siempre en el software, podr铆amos crear nuestra propia librear铆a de Inyecci贸n de Dependencias o utilizar una que ya exista. En mi caso me gusta mucho Inversify de Remo H. Jansen.

Tras mucho tiempo utiliz谩ndola en proyectos reales y en producci贸n, creamos un peque帽o wrapper llamado Inversify-Props que ayuda en el Paso 6 de los ejemplos anteriores.

alt

En la documentaci贸n se pueden observar ejemplos de c贸mo usar la librer铆a, pero analicemos un par de ellos.

    import 'reflect-metadata'; // Import only once
    import { container, inject } from 'inversify-props';

    container.addSingleton<IService1>(Service1);
    container.addSingleton<IService2>(Service2);

    export default class extends Component {
      @inject() service1: IService1;
      @inject() service2: IService2;
    }

Esta librer铆a ya gestiona el contenedor por ti, simplemente lo importas y a帽ades los servicios que quieras, despu茅s en cada componte (de cualquier framework) inyectas la dependencia utilizando decoradores.

En el ejemplo anterior se usa reflection para no tener que utilizar IDs pero es algo totalmente opcional y la librer铆a autogenera IDs para que puedas utilizarlos si no quieres usar reflection.

    import 'reflect-metadata'; // Import only once
    import { cid, container, inject } from 'inversify-props';

    container.addSingleton<IService1>(Service1);

    export default class extends Component {
      @inject(cid.IService1) service1: IService1;
    }

Incluso puedes usar cualquier ID que prefieras.

    import 'reflect-metadata'; // Import only once
    import { container, inject } from 'inversify-props';

    container.addSingleton<IService1>(Service1, 'myid');

    export default class extends Component {
      @inject('myid') service1: IService1;
    }

Para marcar un servicio como candidato a ser inyectado utilizamos el decorador propio de Inversify.

    // iservice1.ts
    export interface IService1 {
        method1(): string;
    }

    // service.ts
    @injectable()
    export class Service1 implements IService1 {
      method1(): string {
        return 'method 1';
      }
    }

Pues esto es todo sobre Inyecci贸n de Dependencias, Inversi贸n de Control e Inversi贸n de Dependencias 驴Qu茅 te parece? 驴Sueles inyectar dependencias en tus proyectos de Frontend? 驴Y en los de Backend? Empieza ahora mismo y disfruta de las facilidades de uno de los patrones m谩s interesantes que podr谩s aprender este a帽o.

BONUS: 驴Usas hooks? No te pierdas la versi贸n simplificada para usar en Hooks.

Descrubre nuestro e-book

Si quieres continuar mejorando como desarrollador Javascript te recomendamos nuestro e-book de Clean Code, SOLID y Testing aplicado a JavaScript .

Profundizamos en temas como la deuda t茅cnica y cuales son los tipos, Clean Code desde el punto de vista de mejorar la legibilidad, SOLID para obtener un c贸digo m谩s intuitivo y tolerante a cambios, y Unit testing para obtener proyectos de mayor calidad y seguridad... Adem谩s, puedes empezar a leer los primeros cap铆tulos gratis.

e-book de Clean Code, SOLID y Testing aplicado a JavaScript