Comprender la inyección de dependencia en PHP

A lo largo de la sección introductoria, tocamos pasar la dependencia a través del método class __construct(). Hay más que simplemente pasar el objeto dependiente.
Consideremos los siguientes tres ejemplos aparentemente similares pero diferentes.

Aunque PHP ha sido compatible con sugerencias de tipo durante bastante tiempo, no es raro encontrar trozos de código, que son los siguientes:

<?php
class App
{
protected $config;
protected $logger;
public function __construct($config, $logger)
{
$this->config = $config;
$this->logger = $logger;
}
public function run()
{
$this->config->setValue('executed_at', time());
$this->logger->log('executed');
}
}
class Config
{
protected $config = [];
public function setValue($path, $value)
{
// implementation
}
}
class Logger
{
public function log($message)
{
// implementation
}
}
$config = new Config();
$logger = new Logger();
$app = new App($config, $logger);
$app->run();

Podemos ver que el método de la clase de aplicación __construct() no utiliza la función de sugerencia de tipo PHP. El desarrollador asume que las variables $config y $logger son de cierto tipo. Si bien este ejemplo funcionará bien, aún mantiene nuestras clases estrechamente unidas. Realmente no hay tanta diferencia entre este ejemplo y el anterior donde teníamos la dependencia $msqli dentro del método loadByEmail().
Agregar sugerencias de tipo a la mezcla nos permite forzar los tipos que pasamos al método de la clase de aplicación __construct():

class App
{
protected $config;
protected $logger;
public function __construct(Config $config, Logger $logger)
{
$this->config = $config;
$this->logger = $logger;
}
public function run()
{
$this->config->setValue('executed_at', time());
$this->logger->log('executed');
}
}
class Config
{
protected $config = [];
public function setValue($path, $value)
{
// implementation
}
}
class Logger
{
public function log($message)
{
// implementation
}
}
$config = new Config();
$logger = new Logger();
$app = new App($config, $logger);
$app->run();

Este simple movimiento nos lleva a la mitad de hacer que nuestro código esté débilmente acoplado. Aunque ahora estamos instruyendo a nuestros objetos inyectables para que sean de un tipo exacto, todavía estamos bloqueados en un tipo específico, es decir, implementación. La lucha por un acoplamiento flojo no debería bloquearnos en una implementación específica; de lo contrario, no habría mucho uso de un patrón de inyección de dependencia.

Este tercer ejemplo establece una diferenciación importante con respecto a los dos primeros ejemplos:

<?php
class App
{
protected $config;
protected $logger;
public function __construct(ConfigInterface $config, LoggerInterface
$logger)
{
$this->config = $config;
$this->logger = $logger;
}
public function run()
{
$this->config->setValue('executed_at', time());
$this->logger->log('executed');
}
}
interface ConfigInterface
{
public function getValue($value);
public function setValue($path, $value);
}
interface LoggerInterface
{
public function log($message);
}
class Config implements ConfigInterface
{
protected $config = [];
public function getValue($value)
{
// implementation
}
public function setValue($path, $value)
{
// implementation
}
}
class Logger implements LoggerInterface
{
public function log($message)
{
// implementation
}
}
$config = new Config();
$logger = new Logger();
$app = new App($config, $logger);
$app->run();

Favorecer sugerencias de tipo de interfaz en lugar de sugerencias de tipo de clase concreta es uno de los ingredientes clave para escribir código acoplado libremente. Aunque todavía estamos inyectando dependencias a través de la clase __construct(), ahora lo estamos haciendo en un programa a una interfaz, no a una manera de implementación. Esto nos permite evitar un acoplamiento estrecho, lo que hace que nuestro código sea más reutilizable.
Claramente, estos ejemplos son en última instancia simples. Podemos imaginar cuán rápido las cosas comienzan a complicarse cuando aumenta el número de objetos inyectados, donde cada uno de los objetos inyectados podría necesitar uno, dos o incluso una docena de los parámetros __construct() en sí. Esto es donde el contenedor de inyección de dependencia es útil.

Comparte