Inyección de dependencias

Inyección de dependencias

Inversión de Control

Para abordar este tema es necesario en primer lugar que hagamos referencia al término de Inversión de Control.

Para entenderlo, hay un tópico bastante famoso que nos indica que el funcionamiento de esto es parecido a como se trabaja en Hollywood (Principio de Hollywood) con los actores noveles. “No nos llames, nosotros te llamaremos”. Es decir, serán las clases mismas las que se encargarán de pedir los objetos necesarios.

Teniendo esto en mente, podemos entender la inversión de control como el proceso donde los objetos son suministrados a las clases que los necesitan, en lugar de crearlos ellos mismos.

Imaginemos que tenemos una clase Newsletter que necesita enviar correos.

Lo normal, como hasta ahora, podría ser algo como esto:

<?php
class Newsletter {
  protected $mail ;
  function __construct() {
    $this -> mail = New Mailer ('correo@gmail.com', ...) ;
  }
}

Pero esto encierra muchos más problemas de lo que parece a simple vista. Estamos creando una dependencia muy fuerte entre la clase Newsletter y la clase Mail.

¿Qué ocurriría si necesitamos utilizar otro objeto Mailer? ¿Y si nos cambia el usuario y la contraseña? ¿Tenemos que cambiarlo en cada sitio? ¿Y sí en algunas clases tenemos que utilizar el objeto Mailer configurado de una forma y en otras de otro?

Una posible solución propuesta por la Inversión de Control es esta:

<?php
class Newsletter {
  protected $mail ;
  function __construct() {
    $this -> mail = $mail ;
  }
}

Lo que hemos hecho, es pasarle un objeto completamente configurado y preparado para enviar correos eliminando esa fuerte dependencia que existía entre las dos clases.

Ahora mismo estarás pensando que el problema no está solucionado todavía, que todavía has tenido que crear ese objeto Mail, configurarlo a mano y pasárselo a cada objeto que lo requiera. ¡Vamos paso a paso!

Inyección de dependencias

La inyección de dependencias es una forma de Inversión de Control. En el ejemplo anterior hemos visto una posible forma de implementar este patrón de diseño. Vamos a ver ahora ahora una alternativa a esta:

<?php
class Newsletter {
  protected $mail ;
  public function setMailer (Mailer $mail) {
    $this -> mail = $mail ;
  }
  
  public function getMailer () {
    return $this -> mail ;
  }
}

Vemos que la diferencia radica en la forma en que pasamos los objetos. En un caso se los hemos pasado por el constructor y en otros casos se los hemos pasado utilizando métodos SET / GET.

El primer método es el adecuado cuando tenemos que suministrar dependencias que son obligatorias para la clase y el segundo método es el correcto para incluir dependencias opcionales.

Uso de interfaces para definir las dependencias

El siguiente paso para conseguir desacoplar nuestras clases lo conseguimos definiendo interfaces.

Previamente, cuando necesitamos inyectar un objeto, lo hemos realizado indicando el tipo de objeto necesario

public function setMailer (Mailer $mail)

De forma que tenemos la tranquilidad de saber que nos pasaran un objeto de esa clase. Pero eso no es muy flexible tampoco, ya que estamos forzando a crear una dependencia demasiado fuerte.

Lo que podemos hacer es crear una interfaz, que defina las operaciones que tienen que tener nuestros objetos que sirva para enviar correos (Pongamos por ejemplo, un método llamado send)

  public function setMailer (MailInterface $mail)

De esta forma tenemos la dependencia fijada de forma mucho más desacoplada pues nos permitirá de forma muy rápida cambiar de un objeto a otro, siempre que cumpla con esa interfaz.

Contenedor de dependencias

El problema que todavía nos queda es que, fuera de los objetos, tenemos que ir construyendo las dependencias a mano, e ir pasándoselas una a una. Necesitamos centralizar el proceso de crear estas dependencias utilizando un contenedor de dependencias.

Una implementación sencilla de este concepto tendría métodos para obtener objetos de tipo MAIL, sobre demanda.

<?php
class ContenedorDeDependencias {
  public static function getMailer () {
    return new Mailer ('correo@gmail.com', ...) ;
  }
}

Partiendo de este concepto tan básico podemos construirnos un contenedor de dependencias que lea los parámetros de configuración de un fichero externo, que ponga en caché los objetos creados de forma que no tenga que crear los objetos varias veces si son varios clientes los que se lo piden.

Existen componentes completamente terminados y desacoplados que ofrecen esta funcionalidad y mucha más. Aquí tenemos el componente de Inyección de Dependencias de Symfony. https://github.com/symfony/DependencyInjection

Ahora, para utilizar este u otro componente ya podremos hacer algo como:

  ContenedorDeDependencias::getMailer ()

Bibliografía

Comentarios

Comentario de Sergio Antonio Ochoa Martinez - 27 de Marzo de 2014 - 04:36
Muy interesante todo esto es nuevo para mi y me servio mucho yo soy programardor java y estoy intentando acostumbrar a php oo. Gracias por el articulo Saludos
Comentario de Juan David Grisales Garzon - 18 de Junio de 2014 - 19:41
Bueno, yo mas que todo trabajo con java, acaso en php no hay que pasarle como parametro al constructor la variable $mail? o acaso esto es un error. si esta bien, no he dicho nada, exitos. <?php 2 class Newsletter { 3 protected $mail ; 4 function __construct() { 5 $this -> mail = New Mailer ('correo@gmail.com', ...) ; 6 } 7 }
Ha habido un error en el envío
Comentario enviado. Será revisado por la moderación antes de ser publicado.

Deja tu comentario

Tu nombre:
Tu email:
Tu comentario: