Patrones de diseño en PHP: no solo palabras de moda, sino tu herramienta para un código limpio
\n\nTodo desarrollador PHP, tarde o temprano, se enfrenta a una situación donde el código se convierte en un "espagueti": las clases se inflan, las dependencias se enredan y agregar una nueva funcionalidad requiere reescribir la mitad del proyecto. ¿Te suena familiar?
\nAquí es donde entran en juego los patrones de diseño en PHP (design patterns): soluciones arquitectónicas probadas para problemas típicos. No son bibliotecas ni fragmentos de código listos para copiar. Son conceptos que te ayudan a organizar la estructura de tu aplicación para que sea flexible, testeable y fácil de mantener.
\nEn este artículo no veremos teoría por teoría. Analizaremos ejemplos reales de código PHP y responderemos la pregunta clave: "¿Cuándo es realmente necesario este patrón y cuándo solo complicará la vida?".
\n\n¿Por qué usar patrones de diseño en PHP?
\nMuchos desarrolladores novatos consideran los patrones como una abstracción innecesaria. Pero en la práctica, resuelven tres problemas clave:
\n- \n
- Reducen el acoplamiento (loose coupling) — las clases dependen menos entre sí, lo que facilita su reemplazo y prueba. \n
- Aumentan la reutilización — no escribes el mismo código en diferentes lugares. \n
- Simplifican la refactorización — cuando los requisitos cambian (y siempre cambian), el patrón permite hacer cambios localmente sin romper todo el sistema. \n
Pero hay un detalle: los patrones deben aplicarse con conciencia. Si tienes una aplicación CRUD simple con 3 tablas, implementar una fábrica compleja o un constructor abstracto es sobreingeniería. Un patrón se justifica cuando anticipas un aumento en la complejidad o cuando la arquitectura actual ya empieza a "doler".
\n\nTop 5 patrones de diseño en PHP con ejemplos
\n\n1. Singleton — cuando el objeto debe ser único
\nCuándo usarlo: Necesitas una única instancia de una clase para toda la aplicación. El ejemplo clásico es la conexión a la base de datos o un logger. Si creas una nueva conexión en cada controlador, corres el riesgo de agotar el límite de conexiones.
\nclass Database {\n private static ?Database $instance = null;\n private PDO $connection;\n\n private function __construct() {\n $this->connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');\n }\n\n // Prohibir clonación y unserialize\n private function __clone() {}\n public function __wakeup() {\n throw new \\Exception("Cannot unserialize singleton");\n }\n\n public static function getInstance(): Database {\n if (self::$instance === null) {\n self::$instance = new self();\n }\n return self::$instance;\n }\n\n public function getConnection(): PDO {\n return $this->connection;\n }\n}\n\n// Uso\n$db = Database::getInstance();\n$pdo = $db->getConnection();\nImportante: Singleton es a menudo criticado porque crea un estado global. En frameworks modernos (Laravel, Symfony), se reemplaza con un contenedor de Inyección de Dependencias. Usa Singleton solo en proyectos simples sin DI.
\n\n2. Factory Method — cuando la creación de objetos es compleja
\nCuándo usarlo: No sabes de antemano qué clase exacta necesitas crear, o el proceso de inicialización del objeto requiere varios pasos. Por ejemplo, la creación de diferentes tipos de notificaciones (Email, SMS, Push).
\ninterface Notification {\n public function send(string $message): void;\n}\n\nclass EmailNotification implements Notification {\n public function send(string $message): void {\n echo "Enviando Email: $message\\n";\n }\n}\n\nclass SMSNotification implements Notification {\n public function send(string $mess