Menu
Coddy logo textTech

Inyección de dependencias

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 57 de 91.

En la lección anterior, viste cómo la composición permite que un Car contenga un objeto Engine. Pero hay un problema al crear dependencias dentro de la clase: hace que el código sea rígido y difícil de probar. Inyección de Dependencias resuelve esto pasando las dependencias desde el exterior en lugar de crearlas internamente.

Compara estos dos enfoques:

<?php
// Sin DI - dependencia creada internamente
class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine(); // Fuertemente acoplado
    }
}

// Con DI - dependencia pasada
class Car {
    public function __construct(private Engine $engine) {}
}

$engine = new Engine();
$car = new Car($engine); // Inyectado desde el exterior

El segundo enfoque es la inyección de dependencias. El Car no crea su propio motor; recibe uno. Este simple cambio tiene beneficios poderosos: puedes pasar diferentes tipos de motores, intercambiar implementaciones para pruebas y la clase se vuelve más flexible.

Para una flexibilidad aún mayor, inyecte interfaces en lugar de clases concretas:

<?php
interface EngineInterface {
    public function start(): string;
}

class Car {
    public function __construct(private EngineInterface $engine) {}
    
    public function start(): string {
        return $this->engine->start();
    }
}

Ahora Car funciona con cualquier clase que implemente EngineInterface - un motor de gas, un motor eléctrico o un mock para pruebas. Este desacoplamiento es la razón por la cual la inyección de dependencias es fundamental para escribir aplicaciones PHP mantenibles y testeables.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de notificación que demuestre el poder de la inyección de dependencias. En lugar de codificar de forma rígida cómo se envían los mensajes, crearás un sistema flexible donde el método de entrega se pueda intercambiar fácilmente.

Organizarás tu código en cuatro archivos:

  • MessageSenderInterface.php — Define una interfaz llamada MessageSenderInterface con un único método send(string $recipient, string $message): string. Esta interfaz establece el contrato que cualquier remitente de mensajes debe seguir.
  • EmailSender.php — Crea una clase EmailSender que implemente MessageSenderInterface. Incluye el archivo de la interfaz. El método send() debe devolver "Email to $recipient: $message".
  • NotificationService.php — Crea una clase NotificationService que reciba su dependencia a través del constructor. Incluye el archivo de la interfaz. La clase debe:
    • Aceptar un MessageSenderInterface en su constructor utilizando la promoción de propiedades del constructor.
    • Tener un método notify(string $recipient, string $message) que delegue en el remitente inyectado y devuelva su resultado.
    Observa cómo NotificationService depende de la interfaz, no de una clase concreta. Esto significa que podrías inyectar cualquier remitente que implemente la interfaz sin cambiar el servicio.
  • main.php — Incluye los archivos EmailSender y NotificationService. Recibirás dos entradas: un destinatario y un mensaje. Crea un EmailSender, inyéctalo en un NotificationService, luego llama a notify() e imprime el resultado.

Este patrón mantiene tu NotificationService flexible: funciona con cualquier remitente que inyectes, ya sea correo electrónico, SMS o algo que aún no hayas construido. El servicio no crea sus propias dependencias; las recibe desde el exterior.

Hoja de referencia

La Inyección de Dependencias (DI) es una técnica en la que las dependencias se pasan a una clase desde el exterior en lugar de crearse internamente.

Sin DI (fuertemente acoplado):

<?php
class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine(); // Creado internamente
    }
}

Con DI (débilmente acoplado):

<?php
class Car {
    public function __construct(private Engine $engine) {}
}

$engine = new Engine();
$car = new Car($engine); // Inyectado desde el exterior

Para una máxima flexibilidad, inyecte interfaces en lugar de clases concretas:

<?php
interface EngineInterface {
    public function start(): string;
}

class Car {
    public function __construct(private EngineInterface $engine) {}
    
    public function start(): string {
        return $this->engine->start();
    }
}

Esto permite que Car funcione con cualquier clase que implemente EngineInterface, lo que hace que el código sea más mantenible y fácil de probar.

Pruébalo tú mismo

<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';

// Leer entrada
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));

// TODO: Crear una instancia de EmailSender
// TODO: Inyectarla en un NotificationService
// TODO: Llamar a notify() e imprimir el resultado

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos