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.
Desafío
FácilVamos 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 llamadaMessageSenderInterfacecon un único métodosend(string $recipient, string $message): string. Esta interfaz establece el contrato que cualquier remitente de mensajes debe seguir.EmailSender.php— Crea una claseEmailSenderque implementeMessageSenderInterface. Incluye el archivo de la interfaz. El métodosend()debe devolver"Email to $recipient: $message".NotificationService.php— Crea una claseNotificationServiceque reciba su dependencia a través del constructor. Incluye el archivo de la interfaz. La clase debe:- Aceptar un
MessageSenderInterfaceen 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.
NotificationServicedepende de la interfaz, no de una clase concreta. Esto significa que podrías inyectar cualquier remitente que implemente la interfaz sin cambiar el servicio.- Aceptar un
main.php— Incluye los archivos EmailSender y NotificationService. Recibirás dos entradas: un destinatario y un mensaje. Crea unEmailSender, inyéctalo en unNotificationService, luego llama anotify()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 exteriorPara 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
?>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
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy