Java: ¿Qué es la Inyección de Dependencias?

En la programación orientada a objetos(POO) existe infinidad de temas que aprender y comprender de manera clara para hacer un buen desarrollo. Uno de los principales temas con los que nos encontramos cuando se hace POO es lo relacionado a la Inyección de Dependencias.

El tema parece ser un poco complejo y la mayoría de las veces cuando nos lo explican por primera vez, se nos hace completamente difícil de entender, pero es una de las cosas que quizá hemos hecho en nuestros desarrollos anteriormente y ni siquiera lo sabemos.

Pues bien, para entender la Inyección de Dependencias es importante saber que en la POO se caracteriza por que los objetos sean lo más independientes posibles, esto significa que queremos que las clases que definen a nuestros objetos sean auto-suficientes a pesar de los cambios de los objetos con los que interactúa.

¿Qué es una dependencia?

Para empezar definamos qué es una dependencia, en la vida real una dependencia entre objetos puede ser la de un auto y una gasolinera. Para que el automóvil funcione, es necesario que el tanque tenga gasolina y es necesario que hacer uso de una gasolinera de manera regular para abastecerlo. Pero la dependencia aunque existente no se reduce únicamente a un solo establecimiento.

Pues siempre que la gasolinera tenga el combustible y funcione correctamente, el automóvil se mantendrá con una fuente de energía y no estará ligado por completo a un solo establecimiento(no se encontrará con una dependencia fuertemente acoplada).

Michael_Schumacher_Ferrari_2004
Si Schumacher fuera una clase, SIN la Inyección de Dependencias no sabría conducir otro automóvil que no fuera Ferrari.

Para responder qué es una dependencia desde el punto de vista de código, usaremos un ejemplo en Java. En este ejemplo tenemos la clase Schumacher  que implementa la interface Piloto, que tiene el método conducir.


public class Schumacher implements Piloto {

   private Vehiculo vehiculo;

   public Schumacher(){
      vehiculo = new Ferrari();
   }

   @Override

   public void pilotar() {
      vehiculo.conducir();
   }
}

En el ejemplo anterior,  la clase Schumacher es dependiente de la interfaz Vehiculo que es implementada por la clase Ferrari en el constructor. Por lo anterior, se puede decir que Shumacher se encuentra fuertemente acoplado a Ferrari, pero qué pasa si queremos que Shumacher sea capaz de pilotar un Lamborghini o un BMW, para este caso la forma de la implementación se encuentra limitada y no es muy flexible. Entonces es cuando la inyección de dependencias llega a rescatarnos, o más bien a Schumacher

Inyección de dependencias

Existen dos formas genéricas, por constructor o por mutador(setter); la inyección de dependencias por constructor es cuando la creación de la instancia no depende del la clase. Y en su lugar el constructor de la clase recibe como argumento un objeto de la clase abstracta Vehículo lo que hace posible que Schumacher sea capaz de pilotar diferentes tipos de vehículos sin estar acoplado a alguno en particular.


public class Schumacher implements Piloto {

   private Vehiculo vehiculo;

   public Persona( Vehiculo v){
      vehiculo = v;
   }

   @Override

   public void pilotar() {
      vehiculo.conducir();
   }
}

Otra manera de inyectar dependencias es mediante el uso de setters, lo que permite mayor flexibilidad. Ya que incluso después de que un objeto de la clase haya sido creado, se podrá asignar un vehículo diferente. Es importante remarcar que en este caso estamos trabajando con una Interfaz, y no con una clase normal, y gracias al polimorfismo el uso de una interfaz nos da la ventaja de que si a mitad de la carrera(la ejecución), decidimos que Schumacher conduzca un nuevo vehículo, entonces será posible cambiar la implementación de la dependencia utilizando un setter y de una forma muy sencilla.


public class Schumacher implements Piloto {

   private Vehiculo vehiculo;

   public Persona( Vehiculo v) {
      vehiculo = v;
   }

   public void setVehiculo(Vehiculo v) {
      vehiculo = v;
   }
   @Override

   public void pilotar() {
      vehiculo.conducir();
   }
}

Y aunque el ejemplo aquí mostrado se aplico a Java, el tema en general aplica en cualquier desarrollo de POO, y que nos dará una ventaja en cuanto a flexibilidad, mantenimiento y facilidad de testeo. Espero que esto les sea de utilidad, y si tienen alguna duda o comentario, pueden externarla en la sección de comentarios.