¿Qué son los patrones de diseño y por qué son necesarios en PHP?
Los patrones de diseño (design patterns) son soluciones probadas para problemas típicos que surgen durante el desarrollo de software. No son código listo para usar, sino plantillas que se pueden adaptar a una situación específica. Para un desarrollador de PHP, el conocimiento de los patrones ayuda a escribir código más flexible, mantenible y escalable.
En este artículo, analizaremos cuatro patrones básicos del catálogo GoF (Gang of Four) que se utilizan con mayor frecuencia en proyectos reales de PHP. Cada patrón irá acompañado de un ejemplo práctico y explicaciones.
1. Singleton
El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella. Es útil para gestionar conexiones a bases de datos, loggers o caché.
¿Cuándo aplicarlo?
- Se necesita exactamente un objeto (por ejemplo, una conexión a la base de datos).
- El objeto se utiliza en toda la aplicación y no se permite la creación de duplicados.
Ejemplo de implementación:
<?phpclass DatabaseConnection{ private static ?self $instance = null; private PDO $pdo;
private function __construct() { $this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); }
public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; }
public function getConnection(): PDO { return $this->pdo; }
// Prohibimos la clonación y deserialización private function __clone() {} public function __wakeup() { throw new \\Exception("Cannot unserialize singleton"); }}
// Uso:$db = DatabaseConnection::getInstance();$pdo = $db->getConnection();?>Importante: Singleton es a menudo criticado por violar el principio de responsabilidad única y complicar las pruebas. Úselo con conciencia y solo donde sea realmente necesario.
2. Factory Method
El patrón Factory Method define una interfaz para crear un objeto, pero permite que las subclases alteren el tipo de objeto creado. Esto permite delegar la lógica de creación de objetos a las clases hijas.
¿Cuándo aplicarlo?
- No se sabe de antemano qué objeto específico se necesitará crear.
- Se desea ocultar la lógica compleja de creación de objetos del cliente.
Ejemplo de implementación:
<?php// Interfaz para todos los tipos de notificacionesinterface Notification{ public function send(string $message): void;}
// Implementaciones concretasclass EmailNotification implements Notification{ public function send(string $message): void { echo "Enviando email: {$message}"; }}
class SMSNotification implements Notification{ public function send(string $message): void { echo "Enviando SMS: {$message}"; }}
// Creador abstractoabstract class NotificationFactory{ abstract public function createNotification(): Notification;
public function notify(string $message): void { $notification = $this->createNotification(); $notification->send($message); }}
// Fábricas concretasclass EmailFactory extends NotificationFactory{ public function createNotification(): Notification { return new EmailNotification(); }}
class SMSFactory extends NotificationFactory{ public function createNotification(): Notification { return new SMSNotification(); }}
// Uso:$factory = new EmailFactory();$factory->notify("¡Su pedido ha sido confirmado!");
$factory = new SMSFactory();$factory->notify("Código de confirmación: 1234");