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 Observador 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, todas sus dependencias (los observadores) reciben una notificación y se actualizan automáticamente. Piensa en ello como una suscripción a un boletín: los suscriptores reciben actualizaciones cada vez que se publica contenido nuevo.
El patrón implica 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 reciben una notificación 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 a alice@mail.com: ¡Noticias de última hora!
// Email a bob@mail.com: ¡Noticias de última hora!El patrón Observer promueve un acoplamiento flexible: el sujeto no necesita conocer las clases concretas de sus observadores, solo que implementan la interfaz Observer. Esto facilita agregar nuevos tipos de observadores sin modificar el sujeto.
Desafío
Fácil¡Construyamos un sistema de alertas de precios de acciones usando el patrón Observer! Crearás un sistema en el que un marcador de cotizaciones notifica a varios inversores cada vez que cambia el precio de una acción, lo que permite 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 implementarán todos los inversores. Debe declarar un único métodoupdate(String stockName, double price)que los observadores usan para recibir notificaciones sobre los 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 mediante el constructor).
Un campo double privado para el precio actual (inicialmente 0).
Una lista privada para almacenar 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 después 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 mediante el constructor. Cuando recibe una actualización, imprime[name] received alert: [stockName] is now $[price] - Making quick trade!LongTermInvestor: tiene un campo de nombre privado establecido mediante el constructor. Cuando recibe una actualización, imprime[name] received alert: [stockName] is now $[price] - Holding steady.Formatea el precio con dos decimales.
Main.java: ¡Une todo tu sistema de observadores! Recibirás tres entradas: el nombre de la acción (String), la primera actualización del precio (double) y la segunda actualización del precio (double).Crea un
StockTickercon el nombre de acción proporcionado. Crea unDayTraderllamado"Alice"y unLongTermInvestorllamado"Bob", y suscribe a ambos al marcador.Establece el precio en el primer valor de entrada (todos los observadores reciben una notificación). Después, cancela la suscripción de Alice. Finalmente, establece el precio en el segundo valor de entrada (esta vez solo Bob debe recibir una notificación).
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: StockTicker no sabe nada sobre los tipos específicos de inversores. Solo sabe que implementan Observer. Cuando cancelas la suscripción de Alice, deja de recibir actualizaciones mientras Bob continúa recibiendo notificaciones. Este acoplamiento flexible facilita añadir nuevos tipos de inversores sin cambiar el marcador.
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: Crea un StockTicker con el nombre de acción dado
// TODO: Crea un DayTrader llamado "Alice"
// TODO: Crea un LongTermInvestor llamado "Bob"
// TODO: Suscribe tanto a Alice como a Bob al ticker
// TODO: Establece el precio a firstPrice (ambos observadores son notificados)
// TODO: Cancela la suscripción de Alice
// TODO: Establece 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 IteratorPractica por tu cuenta: Compilador de Java online