Conceptos sobre Inversión de dependencia, Inversión de control (IoC) e Inyección de dependencias (DI)

Yair Carreno
7 min readApr 26, 2022
Mompox — Bolivar. Taken by Puriy.

Hace un tiempo en mi libro Clean Architecture en iOS presenté las definiciones de los términos Inversión de dependencia, Inversión de control (IoC) e Inyección de dependencias (DI).

Estilos de arquitecturas tales como Clean Architecture, Hexagonal Architecture entre otros estilos, hacen un uso importante de estos principios y patrones de diseño para aportar a los componentes un nivel de desacoplamiento adecuado, facilitar la ejecución de pruebas y agregar un aceptable nivel de mantenibilidad a la solución.

Lo interesante y novedoso, ha sido como las vistas declarativas que son escritas a través de frameworks tales como Jetpack Compose, SwiftUI y Flutter, han incorporado también estos principios de forma natural aprovechando las ventajas de la programación funcional. Ese tema es abordado en otro artículo llamado “Inversión de control en vistas declarativas de aplicaciones móviles: Jetpack Compose y SwiftUI”. Los conceptos expuestos a continuación ayudan a entender su funcionamiento.

El objetivo de este artículo es repasar las definiciones de los términos Inversión de dependencia, Inversión de control (IoC) e Inyección de dependencias (DI) y sobre todo dejar claridad de como se relacionan y diferencian entre ellas.

Inversión de dependencia

La inversión de dependencia es un principio fundamental de diseño sobre el cual se sustenta muchos conceptos usados en diferentes estilos de arquitectura.

Para entender el principio, el lector debe imaginar que tiene una relación entre dos elementos como se muestra en la figura 1.1, en donde el elemento A depende del elemento B.

Figura 1.1 Dependencia entre elementos.

¿Qué significa que el elemento A depende del elemento B?

Significa que el elemento A está utilizando alguna funcionalidad o servicio ofrecido por B para cumplir con alguna tarea, es decir que el elemento A requiere del elemento B para cumplir con sus funciones. Ese tipo de relación es lo que llamamos dependencia.

Ahora bien, ¿Cómo se hace para que la dependencia se invierta, es decir que el elemento A no dependa de B sino, al contrario el elemento B dependa de A?

Esto se logra a través de la adición de un componente que sirva de intermediario entre el elemento A y B.

Dicho componente intermediario es conocido en algunos lenguajes de programación como interfaces (Java, Kotlin) o protocolos (Swift), dependiendo del lenguaje con el que estemos trabajando.

Sin embargo, en lenguajes modernos con aspecto de programación funcional tales como Kotlin, Swift o Dart, podemos contar con otros mecanismos tales como, “higher order functions” usados en conjunto con “lambdas o closures” para hacer cumplir este principio. Esta otra alternativa es detallada en “Inversión de control en vistas declarativas de aplicaciones móviles: Jetpack Compose y SwiftUI”.

La función principal de la interface o protocol (o el componente intermediario) no se limita solo a invertir la relación de dependencia, también permite separar la abstracción de la implementación, es decir desacoplar los componentes.

El desacoplamiento es vital en cualquier arquitectura y es una práctica recomendada en el diseño de componentes de software.

En la figura 1.2 se muestra la nueva relación establecida con la interface agregada.

Figura 1.2 Inversión de dependencia entre elementos.

En este caso, el elemento A utiliza la interface I y a su vez el elemento B depende de la interface I e implementa las operaciones definidas en I.

En esta nueva relación al elemento A ya no le importa lo que ocurra con B ya que solo está relacionado con el elemento I (por agregación), mientras que por otro lado el elemento B si depende del elemento I (por herencia) pero tiene la libertad de implementar las operaciones definidas en I a su conveniencia.

¿Qué ocurriría si fuera necesario reemplazar el elemento B por x o y razón, por ejemplo que el elemento B se volvió obsoleto o requiere una actualización?

En ese caso se lleva a cabo la modificación agregando un nuevo elemento C con la única condición de que debe conformar el elemento I y por lo tanto implementar las operaciones definidas en I.

En la figura 1.3 se muestra la nueva relación:

Figura 1.3 Desacoplamiento e inversión de dependencia entre elementos.

Se puede notar que no es necesario tocar el elemento A gracias al desacoplamiento y la inversión de la dependencia.

Este principio de diseño tiene sus orígenes en el patrón Separated Interface. En las arquitecturas, es muy usual verlo aplicado en las fronteras de las capas de la aplicación. Por ejemplo entre las fronteras de las capas Domain y Data.

En estilos de arquitectura, los elementos tipo I es decir las interfaces o protocols, son conocidos como ports (puertos) y los componentes que los implementan como por ejemplo en este caso B y C, se les conoce como adapters (adaptadores).

Inversión de control

Inversión de control, es el mecanismo usado para hacer cumplir el principio de inversión de dependencia. Este se apoya en la delegación y en una relación de agregación, así como se muestra en la figura 1.4.

Figura 1.4 Relaciones de agregación vs herencia.

La relación entre el elemento A y el elemento I es una relación de agregación mientras que la relación entre el elemento B y el elemento I es de herencia.

La agregación y herencia son dos formas diferentes de delegar o extender las funcionalidades de un componente. Recomiendo al lector mi artículo relacionado en donde profundizo sobre el tema “Mecanismos de delegación usados en soluciones móviles“.

Una práctica recomendada en diseños técnicos es darle preferencia a relaciones de agregación o composición más que a las relaciones de herencia, esto, cuando se requiere extender o delegar funciones a otro elemento.

Aplicar este mecanismo de delegación origina como resultado la aplicación de inversión de control, ya que un elemento puede delegarle la ejecución de una tarea a otro elemento, en otras palabras le entrega el control de una tarea a otro elemento.

Para entender mejor el concepto se muestra la figura 1.5 con un ejemplo de pseudocódigo:

Figura 1.5 Ejemplo de pseudocódigo en inversión de control.

El elemento A, a través de agregación conoce la abstracción de B mas no su implementación. A contiene una tarea llamada taskInA en la cual delega parte de la operación al elemento B a través de la tarea task.

El elemento A desconoce cómo la tarea delegada es ejecutada en el elemento B, solo sabe que el elemento B tiene la capacidad de realizar dicha tarea.

La aplicación de inversión de control también aporta al cumplimiento del principio Single Responsibility, ya que permite que cada elemento se encargue solo de las funciones para las cuales fueron diseñados y así, tenga una única razón de cambio.

Inyección de dependencia

Inyección de dependencia es un patrón de diseño que aplica el mecanismo de inversión de control, de tal forma que un elemento A puede conocer un elemento B a través de una relación de agregación sin que la responsabilidad de la creación de la instancia del elemento B quede del lado del elemento A.

En la figura 1.6 se puede notar que el elemento A conoce de la existencia de un elemento de tipo B que conforma la interfaz I, sin embargo el elemento A desconoce la forma en que el elemento B se instancia o se crea.

Figura 1.6 Inyección de dependencias.

Así que, ¿Quién crea la instancia del elemento B?

Esta tarea es delegada a una entidad o sistema externo comúnmente conocido como Gestor de inyección de dependencias. Un gestor de inyección de dependencias se puede implementar en la aplicación a través de:

• El patrón Service Locator.

• A través de la integración de librerías diseñadas para dicha función de inyectar objetos.

  • A través de la implementación manual haciendo inyección por constructor, por método o por propiedad.

Resumen

Para resumir y dejar clara las relaciones y diferencias entre cada términos tenemos:

Inversión de dependencia: Es un principio de diseño para desacoplar componentes.

Inversión de control: Es el mecanismo usado para hacer cumplir con el principio de inversión de dependencia. Este se apoya también en la delegación de responsabilidades.

Inyección de dependencia: Es un patrón de diseño usado para implementar la inversión de control. Existe otras formas de implementar la inversión de control, la inyección de dependencia es solo una de ellas.

--

--

Yair Carreno

Software engineer with a technical blog about #iOS, #Android, #Angular. Author of “The Clean Way to Use Rx”: https://leanpub.com/the-clean-way-to-use-rx