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 con crear dependencias dentro de la clase: hace que el código sea rígido y difícil de probar. La inyección de dependencias resuelve esto pasando las dependencias desde fuera en lugar de crearlas internamente.
Compara estos dos enfoques:
<?php
// Sin DI - dependencia creada dentro
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Estrechamente acoplado
}
}
// Con DI - dependencia pasada
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Inyectado desde fuera
El segundo enfoque es la inyección de dependencias. El Car no crea su propio motor: recibe uno. Este sencillo cambio ofrece grandes beneficios: puedes pasar distintos tipos de motor, intercambiar implementaciones para realizar pruebas y la clase se vuelve más flexible.
Para una flexibilidad aún mayor, inyecta 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 class que implemente EngineInterface: un motor de gasolina, un motor eléctrico o un mock para las pruebas. Este desacoplamiento es la razón por la que la inyección de dependencias es fundamental para escribir aplicaciones PHP mantenibles y comprobables.
Desafío
FácilConstruyamos un sistema de notificaciones 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 en el que el método de entrega se pueda cambiar 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 debe seguir cualquier remitente de mensajes.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 mediante el constructor. Incluye el archivo de la interfaz. La clase debe:- Aceptar un
MessageSenderInterfaceen su constructor mediante la promoción 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 de EmailSender y NotificationService. Recibirás dos entradas: un destinatario y un mensaje. Crea unEmailSender, inyéctalo en unNotificationService, después llama anotify()e imprime el resultado.
Este patrón mantiene flexible tu NotificationService. Funciona con cualquier remitente que inyectes, ya sea correo electrónico, SMS o algo que todavía no hayas creado. El servicio no crea sus propias dependencias; las recibe desde el exterior.
Pruébalo tú mismo
<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';
// Leer entrada
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));
// TODO: Crea una instancia de EmailSender
// TODO: Inyéctala en un NotificationService
// TODO: Llama a notify() e imprime 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 la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - 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 __unserializeRepaso: 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)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online