Menu
Coddy logo textTech

Patrón Observer

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 94 de 104.

El patrón Observer define una dependencia de uno a muchos entre objetos: cuando un objeto (el subject) cambia de estado, todos sus dependientes (los observers) son notificados automáticamente. Esto es ideal para sistemas de eventos, actualizaciones de UI o cualquier escenario donde múltiples objetos necesiten reaccionar a los cambios.

El patrón involucra dos roles clave: un Sujeto que mantiene una lista de observadores y les notifica, y Observadores que implementan una interfaz de actualización:

#include <iostream>
#include <vector>
#include <algorithm>

class Observer {
public:
    virtual void update(int value) = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<Observer*> observers;
    int state = 0;
    
public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }
    
    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }
    
    void setState(int value) {
        state = value;
        notify();
    }
    
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }
};

class Display : public Observer {
    std::string name;
public:
    Display(const std::string& n) : name(n) {}
    void update(int value) override {
        std::cout << name << " received: " << value << "\n";
    }
};

int main() {
    Subject sensor;
    Display screen1("Screen1"), screen2("Screen2");
    
    sensor.attach(&screen1);
    sensor.attach(&screen2);
    sensor.setState(42);  // Ambas pantallas notificadas
}

Cuando se llama a setState(), el sujeto itera a través de todos los observadores registrados y llama a su método update(). Los observadores pueden adjuntarse o desvincularse en cualquier momento, lo que hace que el sistema sea flexible y esté débilmente acoplado.

Utiliza Observer cuando los cambios en un objeto requieran actualizar otros, y no quieras que estos objetos estén estrechamente acoplados entre sí.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de monitoreo de una Estación Meteorológica utilizando el patrón Observer. Crearás una estación meteorológica que rastrea datos de temperatura y notifica automáticamente a múltiples unidades de visualización cada vez que la temperatura cambia, una aplicación clásica del mundo real de este patrón.

Organizarás tu código en tres archivos:

  • Observer.h: Define la interfaz del observador y un observador de visualización concreto.

    Crea una clase abstracta Observer con un método virtual puro update(double temperature) que los observadores implementan para recibir notificaciones, junto con un destructor virtual.

    Luego, crea una clase TemperatureDisplay que herede de Observer. Cada pantalla tiene un nombre (string) establecido a través de su constructor. Cuando se llama a update(), debe imprimir:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: Crea el sujeto que mantiene la temperatura y notifica a los observadores.

    Tu clase WeatherStation debe:

    • Almacenar una lista de punteros a observadores y la temperatura actual (inicializar a 0.0)
    • Tener un método attach(Observer* obs) para registrar observadores
    • Tener un método detach(Observer* obs) para eliminar observadores
    • Tener un método setTemperature(double temp) que actualice la temperatura y notifique a todos los observadores adjuntos
    • Tener un método privado notify() que llame a update() en cada observador con la temperatura actual
  • main.cpp: Demuestra el patrón Observer en acción.

    Lee tres entradas:

    1. Nombre para la primera pantalla (string)
    2. Nombre para la segunda pantalla (string)
    3. Un valor de temperatura (double)

    Crea una WeatherStation y dos objetos TemperatureDisplay con los nombres proporcionados. Adjunta ambas pantallas a la estación, luego establece la temperatura al valor de entrada. Ambas pantallas deberían recibir automáticamente la actualización e imprimir sus mensajes.

    Después de eso, desvincula la primera pantalla y establece una nueva temperatura que sea 5 grados superior a la de entrada. Solo la segunda pantalla debería recibir esta actualización.

Por ejemplo, con las entradas Kitchen, Bedroom, y 22.5:

Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees

Con las entradas Office, Lobby, y 18.0:

Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees

Observa cómo el patrón Observer crea un sistema débilmente acoplado: la estación meteorológica no necesita saber nada sobre las pantallas específicas, simplemente notifica a quien esté escuchando. Cuando desvinculas un observador, este deja de recibir actualizaciones automáticamente.

Hoja de referencia

El patrón Observer define una dependencia de uno a muchos donde un sujeto notifica a todos sus observadores automáticamente cuando su estado cambia.

El patrón tiene dos roles clave:

  • Subject: Mantiene una lista de observadores y les notifica los cambios de estado
  • Observer: Implementa una interfaz de actualización para recibir notificaciones

Implementación básica:

#include <iostream>
#include <vector>
#include <algorithm>

class Observer {
public:
    virtual void update(int value) = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<Observer*> observers;
    int state = 0;
    
public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }
    
    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }
    
    void setState(int value) {
        state = value;
        notify();
    }
    
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }
};

class Display : public Observer {
    std::string name;
public:
    Display(const std::string& n) : name(n) {}
    void update(int value) override {
        std::cout << name << " received: " << value << "\n";
    }
};

Métodos clave:

  • attach(): Registra un observador para recibir notificaciones
  • detach(): Elimina un observador de la lista de notificaciones
  • notify(): Itera a través de todos los observadores y llama a su método update()

Use Observer cuando los cambios en un objeto requieran actualizar otros sin un acoplamiento fuerte entre ellos.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "WeatherStation.h"

using namespace std;

int main() {
    // Leer entradas
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: Crear un objeto WeatherStation
    
    // TODO: Crear dos objetos TemperatureDisplay con los nombres de entrada
    
    // TODO: Adjuntar ambos visores a la estación meteorológica
    
    // TODO: Establecer la temperatura al valor de entrada
    // (Ambos visores deberían imprimir sus mensajes)
    
    // TODO: Desacoplar el primer visor
    
    // TODO: Establecer la temperatura 5 grados por encima de la entrada
    // (Solo el segundo visor debería imprimir)
    
    return 0;
}
quiz iconPonte a prueba

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