El patrón Observer (Observador) establece una suscripción en la que los objetos pueden observar, y ser notificados de los cambios que ocurren en otros objetos, permitiendo así una comunicación dinámica y acoplamiento flexible entre ellos.
El observador se suscribe al sujeto. Cuando el sujeto cambia su estado, notifica al observador (y a todos los observadores suscriptos) que su estado cambió.
El patrón Observer trata los siguientes problemas:
Acoplamiento entre Sujeto y Observadores: Reduce el acoplamiento entre el objeto que emite una notificación y los objetos que reciben la notificación.
Notificaciones de Cambios de Estado: Facilita la comunicación cuando un objeto necesita ser monitoreado por uno o más objetos.
El patrón Observer propone las siguientes soluciones:
Suscripción: Permite a los observadores suscribirse y desuscribirse a las notificaciones del sujeto.
Notificación Automática: El sujeto notifica automáticamente a todos los observadores cuando su estado cambia.
Este patrón es recomendable cuando:
Desacoplamiento: Permite que los sujetos y los observadores operen independientemente.
Flexibilidad y Reutilización: Facilita la adición de nuevos observadores sin modificar el sujeto.
Notificaciones Automáticas: Los cambios en el sujeto se propagan automáticamente a todos los observadores.
Potencial de Actualizaciones Innecesarias: Los observadores pueden recibir notificaciones en las que no están interesados, lo que puede llevar a ineficiencias.
Gestión de Memoria: Si los observadores no se desvinculan adecuadamente, puede haber problemas de gestión de memoria.
Rendimiento: En sistemas con muchos observadores o actualizaciones frecuentes, el rendimiento puede verse afectado.
Debemos desarrollar un sistema de alerta meteorológica destinado a proveer actualizaciones en tiempo real sobre las condiciones climáticas a varios dispositivos y usuarios.
Este sistema será utilizado por aplicaciones móviles, estaciones meteorológicas locales, servicios de noticias y otros sistemas interesados en datos meteorológicos precisos y actualizados.
El principal desafío es que el sistema pueda notificar eficientemente a todos los dispositivos y servicios suscriptores sobre cambios en las condiciones climáticas, sin tener que adaptarse a las especificidades de cada suscriptor.
Además, queremos que nuestro sistema sea escalable a la adición de nuevos suscriptores en el futuro, sin requerir una reestructuración significativa.
Vamos a usar el patrón Observer porque ofrece una solución elegante para mantener a múltiples suscriptores informados sobre los cambios del sujeto.
Con este patrón, nuestro sistema meteorológico puede operar como un “Sujeto” que mantiene una lista de suscriptores, u “Observers”, y los notifica automáticamente sobre cualquier cambio de estado.
Esto nos permite agregar nuevos tipos de dispositivos y suscriptores sin alterar el núcleo de nuestra lógica de notificación, manteniendo el sistema flexible y fácil de mantener.
Definimos las interfaces Observer y Subject. Creamos el SubjectConcreto para manejar los datos del clima, ante cada cambio va a notificar a los observadores. Creamos dos observadores MobileApp y WeatherStation, que se van a suscribir al sujeto para estar al tanto de las novedades del clima.
Codificamos en Java lo que preparamos en el diagrama.
Definimos la interfaz Subject:
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
Definimos la interfaz el Observer:
interface Observer {
void update(float temperature, float humidity, float pressure);
}
Creamos la clase ConcreteSubject:
class WeatherData implements Subject {
private List observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
observers.remove(o);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
Implementamos los ConcreteObservers:
class MobileApp implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;
public MobileApp(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Mobile App: Temp=" + temperature + " Humidity=" + humidity);
}
}
class WeatherStation implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;
public WeatherStation(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Weather Station: Temp=" + temperature + " Humidity=" + humidity);
}
}
El código cliente:
public class MeteorologicalService {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
MobileApp userApp = new MobileApp(weatherData);
WeatherStation localStation = new WeatherStation(weatherData);
weatherData.setMeasurements(28.3f, 65f, 1013.1f);
weatherData.setMeasurements(26.5f, 70f, 1012f);
}
}
Los participantes que vimos antes son: Subject, ConcreteSubject, Observer, ConcreteObserver:
En este ejemplo pudimos ver en uso el patrón Observer.
Los observadores se registran en el sujeto y se actualizan automáticamente con nuevas mediciones, dando flexibilidad para agregar nuevos observadores.
Se puede usar junto con Observer para simplificar las comunicaciones entre objetos complejos.
Se puede usar para garantizar un único sujeto o un único observador central en el sistema.