Patrón Observer
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 68 de 87.
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 ello como una suscripción a un boletín informativo: los suscriptores reciben actualizaciones cada vez que se publica contenido nuevo.
El patrón involucra dos componentes principales: un sujeto que mantiene una lista de observadores y les notifica los cambios, y observadores que definen un método de actualización para responder a las notificaciones:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class NewsPublisher {
private List<Observer> subscribers = new ArrayList<>();
public void subscribe(Observer observer) {
subscribers.add(observer);
}
public void unsubscribe(Observer observer) {
subscribers.remove(observer);
}
public void publish(String news) {
for (Observer subscriber : subscribers) {
subscriber.update(news);
}
}
}
class EmailSubscriber implements Observer {
private String email;
public EmailSubscriber(String email) {
this.email = email;
}
public void update(String message) {
System.out.println("Email to " + email + ": " + message);
}
}Cuando el editor publica noticias, todos los suscriptores son notificados automáticamente:
NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));
publisher.publish("Breaking news!");
// Salida:
// Email para alice@mail.com: Breaking news!
// Email para bob@mail.com: Breaking news!El patrón Observer promueve el acoplamiento débil: el sujeto no necesita conocer las clases concretas de sus observadores, solo que implementan la interfaz Observer. Esto facilita la adición de nuevos tipos de observadores sin modificar el sujeto.
Desafío
Fácil¡Vamos a construir un sistema de alerta de precios de acciones utilizando el Patrón Observer! Crearás un sistema donde un marcador de acciones (stock ticker) notifica a múltiples inversores cada vez que el precio de la acción cambia, permitiendo que cada inversor reaccione a las actualizaciones de precios a su manera.
Organizarás tu código en cuatro archivos:
Observer.java: Define la interfazObserverque todos los inversores implementarán. Debe declarar un único métodoupdate(String stockName, double price)que los observadores utilizan para recibir notificaciones sobre cambios de precio.StockTicker.java: Crea la clase sujeto que mantiene una lista de observadores y les notifica los cambios de precio. TuStockTickerdebe tener:Un campo String privado para el nombre de la acción (establecido a través del constructor).
Un campo double privado para el precio actual (inicialmente 0).
Una lista privada para almacenar a los observadores suscritos.
Un método
subscribe(Observer observer)que añade un observador a la lista.Un método
unsubscribe(Observer observer)que elimina un observador de la lista.Un método
setPrice(double price)que actualiza el precio y luego notifica a todos los observadores llamando a su métodoupdatecon el nombre de la acción y el nuevo precio.Investors.java: Crea dos tipos diferentes de inversores que implementen la interfazObserver:DayTrader- tiene un campo de nombre privado establecido a través del constructor. Cuando se actualiza, imprime[name] received alert: [stockName] is now $[price] - Making quick trade!LongTermInvestor- tiene un campo de nombre privado establecido a través del constructor. Cuando se actualiza, imprime[name] received alert: [stockName] is now $[price] - Holding steady.Formatea el precio con dos decimales.
Main.java: ¡Une tu sistema de observadores! Recibirás tres entradas: el nombre de la acción (String), la primera actualización de precio (double) y la segunda actualización de precio (double).Crea un
StockTickercon el nombre de la acción dado. Crea unDayTraderllamado"Alice"y unLongTermInvestorllamado"Bob", y suscribe a ambos al marcador.Establece el precio al primer valor de entrada (todos los observadores son notificados). Luego, cancela la suscripción de Alice. Finalmente, establece el precio al segundo valor de entrada (solo Bob debería ser notificado esta vez).
Recibirás tres entradas en orden: nombre de la acción (String), primer precio (double), segundo precio (double).
Este desafío demuestra el poder del Patrón Observer: el StockTicker no sabe nada sobre los tipos específicos de inversores; solo sabe que implementan Observer. Cuando cancelas la suscripción de Alice, ella deja de recibir actualizaciones mientras que Bob continúa siendo notificado. ¡Este acoplamiento débil facilita la adición de nuevos tipos de inversores sin cambiar el marcador!
Hoja de referencia
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.
El patrón involucra dos componentes principales:
- Sujeto: Mantiene una lista de observadores y les notifica los cambios
- Observadores: Definen un método de actualización para responder a las notificaciones
Estructura básica:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer observer) {
observers.add(observer);
}
public void unsubscribe(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
class ConcreteObserver implements Observer {
public void update(String message) {
// Reaccionar a la notificación
}
}Ejemplo de uso:
Subject subject = new Subject();
subject.subscribe(new ConcreteObserver());
subject.notifyObservers("State changed!");
// Todos los observadores suscritos reciben la actualizaciónEl Patrón Observer promueve el acoplamiento débil: el sujeto no necesita conocer las clases concretas de sus observadores, solo que implementan la interfaz Observer. Esto facilita la adición de nuevos tipos de observadores sin modificar el sujeto.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer entradas
String stockName = scanner.nextLine();
double firstPrice = scanner.nextDouble();
double secondPrice = scanner.nextDouble();
// TODO: Crear un StockTicker con el stockName dado
// TODO: Crear un DayTrader llamado "Alice"
// TODO: Crear un LongTermInvestor llamado "Bob"
// TODO: Suscribir tanto a Alice como a Bob al ticker
// TODO: Establecer el precio a firstPrice (ambos observadores son notificados)
// TODO: Desuscribir a Alice
// TODO: Establecer el precio a secondPrice (solo Bob es notificado)
scanner.close();
}
}
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 la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator