Patrón Observer
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 73 de 91.
El Patrón Observer es un patrón de diseño de comportamiento que establece una relación de uno a muchos entre objetos. Cuando un objeto (el sujeto) cambia de estado, todos sus dependientes (los observadores) son notificados y actualizados automáticamente.
Piense en una suscripción a un boletín informativo. El boletín informativo es el sujeto, y los suscriptores son los observadores. Cuando se publica una nueva edición, todos los suscriptores la reciben automáticamente sin que el boletín necesite conocer los detalles de cada suscriptor.
Aquí tienes una implementación básica en PHP:
<?php
interface Observer {
public function update(string $message): void;
}
class Newsletter {
private array $subscribers = [];
public function subscribe(Observer $observer): void {
$this->subscribers[] = $observer;
}
public function publish(string $content): void {
foreach ($this->subscribers as $subscriber) {
$subscriber->update($content);
}
}
}
class EmailSubscriber implements Observer {
public function __construct(private string $email) {}
public function update(string $message): void {
echo "Sending to {$this->email}: $message\n";
}
}
$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");
Salida:
Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!El patrón desacopla el sujeto de sus observadores. La clase Newsletter no necesita saber si está notificando a suscriptores de correo electrónico, suscriptores de SMS o servicios de notificación push. Simplemente llama a update() en cualquier cosa que implemente la interfaz Observer. Esto hace que añadir nuevos tipos de observadores sea sencillo sin necesidad de modificar el código existente.
Desafío
FácilVamos a construir un sistema de alerta de precios de acciones utilizando el Patrón Observer. Cuando el precio de una acción cambia, todos los inversores registrados deben ser notificados automáticamente — una aplicación perfecta del mundo real para este patrón.
Organizarás tu código en cuatro archivos:
Observer.php— Define una interfazObservercon un único métodoupdate(string $stockName, float $price): void. Este es el contrato que todos los observadores deben seguir para recibir notificaciones.Investors.php— Incluye el archivo Observer y crea dos clases que implementen la interfaz:EmailInvestor— Recibe una dirección de correo electrónico en su constructor (usa promoción de constructor). Su métodoupdate()imprime"Email to {$this->email}: {$stockName} is now \${$price}\n"SmsInvestor— Recibe un número de teléfono en su constructor. Su métodoupdate()imprime"SMS to {$this->phone}: {$stockName} is now \${$price}\n"
Stock.php— Incluye el archivo Observer y crea una claseStockque actúe como el sujeto. Tu acción debe:- Almacenar el nombre de la acción y el precio actual (usa promoción de constructor para el nombre, inicializa el precio a
0.0) - Mantener un array privado de observadores
- Tener un método
subscribe(Observer $observer): voidpara añadir observadores - Tener un método
setPrice(float $price): voidque actualice el precio y notifique a todos los observadores
update()de cada observador con el nombre de la acción y el nuevo precio.- Almacenar el nombre de la acción y el precio actual (usa promoción de constructor para el nombre, inicializa el precio a
main.php— Incluye los archivos Investors y Stock. Recibirás tres entradas: un nombre de acción, una dirección de correo electrónico y un número de teléfono.Crea un
Stockcon el nombre dado. Suscribe unEmailInvestorcon la dirección de correo electrónico y unSmsInvestorcon el número de teléfono.Establece el precio de la acción a
150.50para activar las notificaciones a todos los suscriptores.
El Patrón Observer mantiene tu clase Stock desacoplada de los métodos de notificación específicos. Podrías añadir fácilmente un PushInvestor o SlackInvestor más tarde sin modificar la clase Stock en absoluto.
Hoja de referencia
El Patrón Observer establece una relación de uno a muchos donde un sujeto notifica automáticamente a todos los observadores cuando su estado cambia.
Componentes Clave:
- Interfaz Observer — Define el contrato para los observadores con un método
update() - Sujeto (Subject) — Mantiene una lista de observadores y les notifica los cambios de estado
- Observadores Concretos — Implementan la interfaz Observer para recibir notificaciones
Implementación Básica:
<?php
interface Observer {
public function update(string $message): void;
}
class Subject {
private array $observers = [];
public function subscribe(Observer $observer): void {
$this->observers[] = $observer;
}
public function notify(string $data): void {
foreach ($this->observers as $observer) {
$observer->update($data);
}
}
}
class ConcreteObserver implements Observer {
public function __construct(private string $name) {}
public function update(string $message): void {
echo "Notifying {$this->name}: $message\n";
}
}
$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");
Beneficios:
- Desacopla el sujeto de sus observadores
- Es fácil añadir nuevos tipos de observadores sin modificar el código existente
- Soporta suscripción/cancelación de suscripción dinámica
Pruébalo tú mismo
<?php
require_once 'Investors.php';
require_once 'Stock.php';
// Leer entradas
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));
// TODO: Crear una Acción (Stock) con el nombre dado
// TODO: Suscribir un EmailInvestor con la dirección de correo electrónico
// TODO: Suscribir un SmsInvestor con el número de teléfono
// TODO: Establecer el precio de la acción a 150.50 para activar las notificaciones
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy