Implementando Observadores Personalizados con Reflection en PHP
En este post, exploraremos una técnica avanzada para implementar un sistema de observadores (Observer pattern) altamente configurable y dinámico en PHP, utilizando Reflection para descubrir y registrar métodos observadores automáticamente. Esto permite una mayor flexibilidad y reduce la necesidad de configuración manual extensa.
Normalmente, el patrón Observer implica suscribir objetos a un sujeto (Subject) y, cuando el estado del sujeto cambia, notificar a todos los observadores. Este enfoque va un paso más allá, permitiendo que los observadores definan múltiples métodos observadores y que el sujeto los invoque dinámicamente basándose en un "evento" o "notificación" específico.
<?php
interface ObserverInterface {
// Marcador para identificar Observadores
}
class Subject {
private array $observers = [];
public function attach(ObserverInterface $observer): void
{
$this->observers[] = $observer;
}
public function detach(ObserverInterface $observer): void
{
$this->observers = array_filter($this->observers, fn($obs) => $obs !== $observer);
}
public function notify(string $event, mixed $data = null): void
{
foreach ($this->observers as $observer) {
$reflection = new ReflectionClass($observer);
$methodName = 'on' . ucfirst($event); // Ejemplo: onUserCreated, onOrderShipped
if ($reflection->hasMethod($methodName)) {
$method = $reflection->getMethod($methodName);
$method->invoke($observer, $data); // Invocamos el método con la data
}
}
}
}
// Ejemplo de Observer
class UserObserver implements ObserverInterface {
public function onUserCreated(array $userData): void
{
echo "Nuevo usuario creado: " . $userData['email'] . "\n";
}
public function onUserDeleted(array $userData): void
{
echo "Usuario eliminado: " . $userData['email'] . "\n";
}
}
// Ejemplo de uso
$subject = new Subject();
$userObserver = new UserObserver();
$subject->attach($userObserver);
$userData = ['email' => 'test@example.com', 'name' => 'Test User'];
$subject->notify('UserCreated', $userData);
$subject->notify('UserDeleted', $userData);
?>
El código anterior muestra una implementación básica. La clave está en el método notify
. Utiliza ReflectionClass
para inspeccionar cada observador adjunto. Construye el nombre del método a llamar basándose en el evento notificado (por ejemplo, transforma "UserCreated" en "onUserCreated"). Luego, verifica si el observador tiene un método con ese nombre. Si existe, lo invoca utilizando $method->invoke($observer, $data)
, pasando la información relevante ($data
) al método observador.
Este enfoque permite una gran extensibilidad. Simplemente implemente la interfaz ObserverInterface
, defina los métodos observadores (por ejemplo, onProductUpdated
, onOrderCancelled
) y adjunte el observador al sujeto. No es necesario modificar la lógica del sujeto para cada nuevo tipo de evento o observador.
// Ejemplo de otro Observer que reacciona a diferentes eventos.
class OrderObserver implements ObserverInterface {
public function onOrderCreated(array $orderData): void {
echo "Nuevo pedido creado con ID: " . $orderData['id'] . "\n";
}
public function onOrderShipped(array $orderData): void {
echo "Pedido enviado con ID: " . $orderData['id'] . "\n";
}
}
$orderObserver = new OrderObserver();
$subject->attach($orderObserver);
$orderData = ['id' => 123, 'total' => 99.99];
$subject->notify('OrderCreated', $orderData);
$subject->notify('OrderShipped', $orderData);
Al usar Reflection, logramos un sistema de observadores mucho más dinámico y adaptable. Podemos agregar nuevos eventos y observadores sin modificar la lógica central del sujeto, lo que mejora la mantenibilidad y la escalabilidad de la aplicación. Recuerde considerar el impacto en el rendimiento de usar Reflection y optimice cuando sea necesario, posiblemente usando caché para los metadatos reflejados.
No hay comentarios:
Publicar un comentario