Menu
Coddy logo textTech

Паттерн Observer

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 94 из 104.

Паттерн Observer определяет зависимость «один ко многим» между объектами: когда один объект ( subject ) изменяет состояние, все его зависимые объекты ( observers ) автоматически получают уведомление. Это идеально подходит для систем событий, обновлений пользовательского интерфейса или любых сценариев, в которых нескольким объектам необходимо реагировать на изменения.

Паттерн включает две ключевые роли: Subject, который поддерживает список наблюдателей и уведомляет их, и Observers, которые реализуют интерфейс обновления:

#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);  // Оба дисплея уведомлены
}

Когда вызывается setState(), субъект перебирает всех зарегистрированных наблюдателей и вызывает их update() method. Наблюдатели могут attach или detach в любое время, что делает систему гибкой и слабосвязанной.

Используйте Observer, когда изменения одного объекта требуют обновления других, а вы не хотите, чтобы эти объекты были тесно связаны друг с другом.

challenge icon

Задание

Легко

Давайте создадим систему мониторинга Weather Station с использованием паттерна Observer. Вы создадите метеостанцию, которая отслеживает данные о температуре и автоматически уведомляет несколько блоков отображения при каждом изменении температуры: классическое применение этого паттерна в реальном мире.

Вы организуете код в трёх файлах:

  • Observer.h: Определите интерфейс Observer и конкретный объект отображения.

    Создайте абстрактный Observer class с чистым виртуальным методом update(double temperature), который observers реализуют для получения уведомлений, а также с виртуальным деструктором.

    Затем создайте TemperatureDisplay class, который наследуется от Observer. У каждого блока отображения есть имя (строка), задаваемое через его constructor. Когда вызывается update(), он должен вывести:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: Создайте субъект, который хранит температуру и уведомляет observers.

    Ваш WeatherStation class должен:

    • Хранить list указателей на observers и текущую температуру (initialize значением 0.0)
    • Иметь метод attach(Observer* obs) для регистрации observers
    • Иметь метод detach(Observer* obs) для удаления observers
    • Иметь метод setTemperature(double temp), который обновляет температуру и уведомляет всех прикреплённых observers
    • Иметь закрытый метод notify(), который вызывает update() для каждого observer с текущей температурой
  • main.cpp: Продемонстрируйте паттерн Observer в действии.

    Считайте три inputs:

    1. Имя для первого блока отображения (строка)
    2. Имя для второго блока отображения (строка)
    3. Значение температуры (double)

    Создайте WeatherStation и два TemperatureDisplay objects с указанными именами. Прикрепите оба блока отображения к станции, затем установите температуру, равную входному значению. Оба блока отображения должны автоматически получить обновление и вывести свои messages.

    После этого отсоедините первый блок отображения и установите новую температуру, которая на 5 градусов выше входного значения. Только второй блок отображения должен получить это обновление.

Например, для inputs Kitchen, Bedroom и 22.5:

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

Для inputs Office, Lobby и 18.0:

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

Обратите внимание, как паттерн Observer создаёт слабосвязанную систему: метеостанции не нужно ничего знать о конкретных блоках отображения — она просто уведомляет всех, кто слушает. Когда вы отсоединяете observer, он автоматически перестаёт получать обновления.

Попробуйте сами

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

using namespace std;

int main() {
    // Считать входные данные
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: Создать объект WeatherStation
    
    // TODO: Создать два объекта TemperatureDisplay с входными именами
    
    // TODO: Присоединить оба дисплея к метеостанции
    
    // TODO: Установить температуру на входное значение
    // (Оба дисплея должны вывести свои сообщения)
    
    // TODO: Отсоединить первый дисплей
    
    // TODO: Установить температуру на 5 градусов выше входного значения
    // (Только второй дисплей должен вывести)
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++