El uso de interfaces en la inyección de dependencias.

Ya hay mucha documentación en internet sobre los problemas de las dependencias en el código. Por ejemplo:

class MiControllador {
   public function usuarios() {
       $modelo = new LibreriaDeUsuarios();
       return $modelo->obtenerUsuarios();
      }
}

El porqué este código es malo lo explica muy bien Michael Pratt en su blog. En esta entada quiero destacar un punto que Michael pasa muy rápido: el uso de las interfaces en la inyección de dependencias. Sobre todo pensando en proyectos grandes o complejos.

Un código mucho mejor que el anterior sería:

class MiControllador {
   protected $model;
  
   public function __construct(LibreriaDeUsuarios $libreria){
       $this->model = $libreria;

   }
   public function usuarios() { 
       $modelo = new Modelo();
       return $modelo->obtenerUsuarios();
  }
}

$libreria = new LibreriaDeUsuarios();
$micontrolador = new MiControlador($libreria);
$micontrolador->usuarios();

Pero esto todavía presenta problemas si en algún momento queremos dejar de usar LibreriaDeUsuarios para usar OtraLibreriaDeUsuarios (en el constructor de MiControlador especificamos que ha de ser un objeto del tipo LibreriaDeUsuarios).

La solución es definir la dependencia con una interface:

interface ILibrerias {
    public function obtenerUsuarios();
}

class MiControllador {
   protected $model;
  
   public function __construct(ILibrerias $libreria){
       $this->model = $libreria;

   }
   public function usuarios() { 
       $modelo = new Modelo();
       return $modelo->obtenerUsuarios();
  }
}

De esta forma podemos pasarle al constructor cualquier objeto que implemetne la interface requerida.

El problema ahora es que podemos necesitar usar clases de terceros, que no queramos modificar.  Para evitarlo podemos hacer uso del patrón adapter, que nos permite reutilizar una clase con una interfaz diferente, lo que permite ser utilizada por un sistema que utiliza diferentes métodos de llamada.

interface ILibrerias {
    public function obtenerUsuarios();
}


class LibreriasAdapter implements Ilibrerias {
    protected $model;
    public function __construct(LibreriaDeUsuarios $libreria) {
        $this->geocoder = $libreria;
    }
    public function geocode($address) {
      return $this->model->obtenerUsuarios();
    }
}

class MiControllador {
   protected $model;
  
   public function __construct(ILibrerias $libreria){
       $this->model = $libreria;

   }
   public function usuarios() { 
       $modelo = new Modelo();
       return $modelo->obtenerUsuarios();
  }
}

De esta forma temos un código completamente desacoplado. Sólo tendríamos que modificar/ampliar LibreriasAdapter para usar otras librerías.

Posted in php