Inyección de dependencias
En informática, inyección de dependencias (en inglés Dependency Injection, DI) es un patrón de diseño orientado a objetos, en el que se suministran objetos a una clase en lugar de ser la propia clase la que cree dichos objetos. Esos objetos cumplen contratos que necesitan nuestras clases para poder funcionar (de ahí el concepto de dependencia). Nuestras clases no crean los objetos que necesitan, sino que se los suministra otra clase 'contenedora' que inyectará la implementación deseada a nuestro contrato.[1]
En otras palabras, se trata de un patrón de diseño que se encarga de extraer la responsabilidad de la creación de instancias de un componente para delegarla en otro. El término fue acuñado por primera vez por Martin Fowler.
Motivación
En los comienzos de la programación, los programas eran lineales y monolíticos. El flujo de ejecución era simple y predecible, ejecutándose línea tras línea.
Aparecieron dos conceptos para estructurar el código: la modularidad y la reutilización de los componentes: se crean bibliotecas de componentes reutilizables. El flujo se complica, saltando de componente a componente, y aparece un nuevo problema: la dependencia (acoplamiento) entre los componentes.
Las dependencias en el software son necesarias. La problemática con estas viene dada por el grado de acoplamiento que tiene la dependencia con el componente. Lo ideal es tratar de favorecer un grado de acoplamiento bajo, pero sin sacrificar la cohesión.
El problema se empieza a considerar lo suficientemente importante como para definir nuevos conceptos en el diseño como la Inversión de Control (IoC) implementada a través de Inyección de Dependencias o de eventos.
Implementación del patrón en Java
El contenedor
La forma habitual de implementar este patrón es mediante un "Contenedor DI", también llamado "Contenedor IoC" y objetos planos o simples por ejemplo los llamados POJO en Java. El contenedor inyecta a cada objeto los objetos necesarios según las relaciones de dependencia registradas en la configuración previa.
Típicamente este contenedor es implementado por un framework externo a la aplicación (como Spring entre otros), por lo cual en la aplicación también se utilizará inversión de control al ser el contenedor (almacenado en una biblioteca) quien invoque el código de la aplicación.
Declaración de la inyección
La inyección de dependencias puede realizarse referenciando a las clases de dichas dependencias. Sin embargo, esa no es una buena práctica dado que sus componentes tienen una fuerte relación entre sí, que al final nos supondrá un inconveniente para el mantenimiento del software. Por eso, en la inyección de dependencias, normalmente, se usan interfaces. De esta forma conseguimos abstraer la relación entre una clase A que depende de una clase B sin importar la implementación de cada uno de los dos. De esta forma, conseguimos desacoplamiento.[1]
Formas de inyectar las dependencias
En java, y en general en los distintos lenguajes hay distintas formas de inyectar dependencias.[2] Esto se puede lograr de forma manual o mediante frameworks como Spring, para lograr esta versatilidad, la inyección de dependencias se apoya en la programación orientada a interfaces.[3]
En el constructor
Usando el constructor del objeto. Por ejemplo si una clase A tiene la dependencia de un objeto con la interfaz B
public class A {
private B dependency;
public A(B instancedepency)
{
this.dependency=instancedepency;
}
//... En su implementación A usa el objeto dependency
}
En un método
Usando un método, típicamente el método suele ser un setter.
public class A {
private B dependency;
public setDependecy(B instancedepency)
{
this.dependency=instancedepency;
}
//... En su implementación A usa el objeto dependency
}
En una variable de instancia
Usando una variable de instancia o propiedad.
public class A {
public B dependency;
//... En su implementación A usa el objeto dependency
}
Ejemplo
El siguiente ejemplo muestra una implementación sin inyección de dependencias.
public class Vehiculo {
private Motor motor = new Motor();
/** @return retorna la velocidad del vehículo*/
public Double enAceleracionDePedal(int presionDePedal) {
motor.setPresionDePedal(presionDePedal);
int torque = motor.getTorque();
Double velocidad = ... //realiza el cálculo
return velocidad;
}
}
//se omite la clase Motor ya que no es relevante para este ejemplo
La implementación de arriba necesita crear una instancia de Motor para calcular su velocidad. El siguiente ejemplo sencillo muestra una implementación realizando inyección de dependencias usando un método setter.
public class Vehiculo {
private Motor motor = null;
public void setMotor(Motor motor){
this.motor = motor;
}
/** @retorna la velocidad del vehículo*/
public Double enAceleracionDePedal(int presionDePedal) {
Double velocidad = null;
if (null != motor){
motor.setPresionDePedal(presionDePedal);
int torque = motor.getTorque();
velocidad = ... //realiza el cálculo
}
return velocidad;
}
}
//se omite la clase Motor ya que no es relevante para este ejemplo
public class VehiculoFactory {
public Vehiculo construyeVehiculo() {
Vehiculo vehiculo = new Vehiculo();
Motor motor = new Motor();
vehiculo.setMotor(motor);
return vehiculo;
}
}
En este ejemplo VehiculoFactory representa al proveedor. Es una aplicación sencilla del patrón de diseño factory que hace posible que la clase Vehículo no requiera saber cómo obtener un motor por sí misma, sino que es la responsabilidad de VehiculoFactory.
Véase también
Referencias
- Inyección de dependencias vs Inversión de control. ITBLOGSOGETI. 20 de octubre de 2015
- Aclarando conceptos relacionados con la Inyección de Dependencias. Jorge Sánchez. 23 de octubre de 2016
- «Inyección De Dependencias | Random Project». Random Project. 23 de julio de 2018. Archivado desde el original el 1 de octubre de 2018. Consultado el 1 de octubre de 2018.