Inyección de Dependencias, Contenedor IoC y Patrones Singleton / Factory
Aprende a eliminar el acoplamiento rígido con Inyección de Dependencias (DI) y domina los dos patrones creacionales más populares en backend: Singleton y Factory.
El Problema del Acoplamiento Rígido
Cuando una clase instancia sus propias dependencias internas usando new dentro de sus métodos:
class RegistroUsuario {
public function registrar($datos) {
$mailer = new SendGridMailer(); // ❌ ACOPLAMIENTO DURO
$mailer->enviar(...);
}
}
Tu código queda atado de pies y manos:
- No puedes probar
RegistroUsuarioen tests unitarios sin enviar correos reales por Internet. - Si quieres cambiar de SendGrid a Mailgun o AWS SES, tienes que modificar todas las clases del sistema.
Inyección de Dependencias (Dependency Injection)
La Inyección de Dependencias invierte la responsabilidad: en lugar de que la clase fabrique el objeto que necesita, lo recibe listo por el constructor:
class RegistroUsuario {
public function __construct(
private MailerInterface $mailer // ✔ La dependencia se inyecta desde fuera
) {}
}
Patrones Creacionales Clave
| Patrón de Diseño | Propósito Arquitectónico | Estructura Típica |
|---|---|---|
| Singleton (Instancia Única) | Garantiza que exista una única instancia de la clase en toda la petición (ej: conexión a la base de datos). | Constructor private, variable estática $instance y método getInstance(). |
| Factory (Factoría) | Centraliza la lógica para crear y configurar diferentes objetos según parámetros dinámicos. | Método estático crear(string $tipo) que devuelve la instancia correspondiente. |
⚡ Implementación de Patrón Singleton y Factory en PHP Puro
Ejecuta el código en el Sandbox y observa cómo Singleton reutiliza una única conexión y Factory construye transportes:
<?php
declare(strict_types=1);
// ==========================================
// 1. PATRÓN SINGLETON (Instancia Única)
// ==========================================
final class ConexionConfig
{
private static ?self $instancia = null;
private string $idSesion;
// Constructor privado: Impide usar 'new ConexionConfig()' desde fuera
private function __construct()
{
$this->idSesion = bin2hex(random_bytes(4)); // ID aleatorio único
}
// Método de acceso global
public static function getInstancia(): self
{
if (self::$instancia === null) {
self::$instancia = new self();
}
return self::$instancia;
}
public function getId(): string
{
return $this->idSesion;
}
// Prohibir clonación y deserialización
private function __clone() {}
}
// ==========================================
// 2. PATRÓN FACTORY (Factoría de Objetos)
// ==========================================
interface TransporteInterface
{
public function entregar(): string;
}
final class CamionTransporte implements TransporteInterface
{
public function entregar(): string
{
return "🚚 Entrega terrestre por camión de carga.";
}
}
final class BarcoTransporte implements TransporteInterface
{
public function entregar(): string
{
return " Cargo marítimo internacional en contenedor.";
}
}
final class TransporteFactory
{
public static function crear(string $tipo): TransporteInterface
{
return match(strtolower($tipo)) {
'terrestre' => new CamionTransporte(),
'maritimo' => new BarcoTransporte(),
default => throw new InvalidArgumentException("Tipo de transporte no soportado: {$tipo}")
};
}
}
// ==========================================
// PRUEBA EN EL SANDBOX
// ==========================================
echo "<h2>1. Prueba del Patrón Singleton:</h2>";
$config1 = ConexionConfig::getInstancia();
$config2 = ConexionConfig::getInstancia();
echo "<p>ID Sesión Configuración 1: <strong>#{$config1->getId()}</strong></p>";
echo "<p>ID Sesión Configuración 2: <strong>#{$config2->getId()}</strong></p>";
if ($config1 === $config2) {
echo "<p style='color:#10b981; font-weight:bold;'>✔ Ambas variables apuntan a la EXACTA misma instancia en memoria RAM.</p>";
}
echo "<hr><h2>2. Prueba del Patrón Factory:</h2>";
$envioA = TransporteFactory::crear('terrestre');
$envioB = TransporteFactory::crear('maritimo');
echo "<p>" . $envioA->entregar() . "</p>";
echo "<p>" . $envioB->entregar() . "</p>";
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección¿Por qué la Inyección de Dependencias a través del constructor mejora drásticamente las pruebas unitarias de software?
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.🚀 ¿Quieres dominar Manual de Programación Orientada a Objetos en PHP (POO) y crear proyectos reales?
Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.
🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.