Tema 14 · Patrones Creacionales

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 RegistroUsuario en 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:

PatronesCreacionales.php
Solo lectura
<?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>";
Pruébalo tú mismo » Se abre en una nueva pestaña con ejecución en vivo
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿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.