Menu
Coddy logo textTech

Padrão Observer

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 94 de 104.

O padrão Observer define uma dependência de um-para-muitos entre objetos: quando um objeto (o subject) muda de estado, todos os seus dependentes (os observers) são notificados automaticamente. Isso é ideal para sistemas de eventos, atualizações de UI ou qualquer cenário onde múltiplos objetos precisam reagir a mudanças.

O padrão envolve dois papéis principais: um Subject que mantém uma lista de observadores e os notifica, e Observers que implementam uma interface de atualização:

#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);  // Ambos os displays notificados
}

Quando o setState() é chamado, o sujeito itera por todos os observadores registrados e chama o método update() deles. Os observadores podem se anexar ou desanexar a qualquer momento, tornando o sistema flexível e fracamente acoplado.

Use o Observer quando mudanças em um objeto exigirem a atualização de outros, e você não quiser que esses objetos estejam fortemente acoplados.

challenge icon

Desafio

Fácil

Vamos construir um sistema de monitoramento de uma Estação Meteorológica usando o padrão Observer. Você criará uma estação meteorológica que rastreia dados de temperatura e notifica automaticamente várias unidades de exibição sempre que a temperatura muda — uma aplicação clássica do mundo real deste padrão.

Você organizará seu código em três arquivos:

  • Observer.h: Define a interface do observador e um observador de exibição concreto.

    Crie uma classe abstrata Observer com um método virtual puro update(double temperature) que os observadores implementam para receber notificações, junto com um destrutor virtual.

    Em seguida, crie uma classe TemperatureDisplay que herda de Observer. Cada exibição tem um nome (string) definido através de seu construtor. Quando update() for chamado, ele deve imprimir:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: Crie o sujeito que mantém a temperatura e notifica os observadores.

    Sua classe WeatherStation deve:

    • Armazenar uma lista de ponteiros de observadores e a temperatura atual (inicialize com 0.0)
    • Ter um método attach(Observer* obs) para registrar observadores
    • Ter um método detach(Observer* obs) para remover observadores
    • Ter um método setTemperature(double temp) que atualiza a temperatura e notifica todos os observadores anexados
    • Ter um método privado notify() que chama update() em cada observador com a temperatura atual
  • main.cpp: Demonstre o padrão Observer em ação.

    Leia três entradas:

    1. Nome para a primeira exibição (string)
    2. Nome para a segunda exibição (string)
    3. Um valor de temperatura (double)

    Crie uma WeatherStation e dois objetos TemperatureDisplay com os nomes fornecidos. Anexe ambas as exibições à estação e, em seguida, defina a temperatura para o valor de entrada. Ambas as exibições devem receber automaticamente a atualização e imprimir suas mensagens.

    Depois disso, desanexe a primeira exibição e defina uma nova temperatura que seja 5 graus superior à entrada. Apenas a segunda exibição deve receber esta atualização.

Por exemplo, com as entradas Kitchen, Bedroom e 22.5:

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

Com as entradas Office, Lobby e 18.0:

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

Observe como o padrão Observer cria um sistema fracamente acoplado — a estação meteorológica não precisa saber nada sobre as exibições específicas, ela apenas notifica quem estiver ouvindo. Quando você desanexa um observador, ele para de receber atualizações automaticamente.

Folha de consulta

O padrão Observer define uma dependência de um para muitos, onde um sujeito notifica todos os seus observadores automaticamente quando seu estado muda.

O padrão possui dois papéis principais:

  • Subject: Mantém uma lista de observadores e os notifica sobre mudanças de estado
  • Observer: Implementa uma interface de atualização para receber notificações

Implementação 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 principais:

  • attach(): Registra um observador para receber notificações
  • detach(): Remove um observador da lista de notificações
  • notify(): Itera por todos os observadores e chama seu método update()

Use o Observer quando mudanças em um objeto exigirem a atualização de outros sem um acoplamento forte entre eles.

Experimente você mesmo

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

using namespace std;

int main() {
    // Ler as entradas
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: Criar um objeto WeatherStation
    
    // TODO: Criar dois objetos TemperatureDisplay com os nomes de entrada
    
    // TODO: Anexar ambos os displays à estação meteorológica
    
    // TODO: Definir a temperatura para o valor de entrada
    // (Ambos os displays devem imprimir suas mensagens)
    
    // TODO: Desanexar o primeiro display
    
    // TODO: Definir a temperatura para 5 graus acima da entrada
    // (Apenas o segundo display deve imprimir)
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos