Event Bus и паттерн Observer в C++: реализация и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Event Bus и паттерн Observer в C++: от теории к практике

В современной разработке программного обеспечения на C++ важную роль играет слабая связность компонентов. Паттерн Observer (Наблюдатель) и его эволюция — Event Bus (Шина событий) — предоставляют элегантный механизм для организации взаимодействия между объектами без создания жёстких зависимостей. В этой статье мы подробно разберём, как реализовать эти паттерны на C++, рассмотрим их преимущества и ограничения, а также приведём практические примеры кода.

Паттерн Observer: классическая реализация

Паттерн Observer определяет отношение "один ко многим" между объектами: когда один объект (субъект) изменяет своё состояние, все зависимые от него объекты (наблюдатели) автоматически уведомляются об этом. В C++ классическая реализация выглядит следующим образом:

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

// Интерфейс наблюдателя
class IObserver {
public:
    virtual ~IObserver() = default;
    virtual void update(const std::string& message) = 0;
};

// Конкретный наблюдатель
class ConcreteObserver : public IObserver {
private:
    std::string name;
public:
    explicit ConcreteObserver(const std::string& n) : name(n) {}
    void update(const std::string& message) override {
        std::cout << "[" << name << "] Получено: " << message << std::endl;
    }
};

// Субъект (наблюдаемый объект)
class Subject {
private:
    std::vector<std::shared_ptr<IObserver>> observers;
    std::string state;
public:
    void attach(std::shared_ptr<IObserver> observer) {
        observers.push_back(observer);
    }
    void detach(std::shared_ptr<IObserver> observer) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), observer),
            observers.end()
        );
    }
    void notify() {
        for (auto& observer : observers) {
            observer->update(state);
        }
    }
    void setState(const std::string& newState) {
        state = newState;
        notify();
    }
};

int main() {
    Subject subject;
    auto obs1 = std::make_shared<ConcreteObserver>("Наблюдатель 1");
    auto obs2 = std::make_shared<ConcreteObserver>("Наблюдатель 2");
    
    subject.attach(obs1);
    subject.attach(obs2);
    
    subject.setState("Событие #1 произошло!");
    subject.detach(obs1);
    subject.setState("Событие #2 без первого наблюдателя");
    
    return 0;
}

Event Bus: централизованная шина событий

Event Bus расширяет идею Observer, предоставляя централизованный механизм обмена сообщениями. Вместо того чтобы каждый субъект хранил список наблюдателей, все подписки и публикации проходят через единую шину. Это позволяет:

  • Уменьшить связанность — отправители и получатели ничего не знают друг о друге.
  • Упростить масштабирование — новые типы событий добавляются без изменения существующего кода.
  • Обеспечить гибкость — можно добавлять и удалять подписчиков в runtime.

Пример реализации Event Bus на C++ с использованием шаблонов и std::function:

#include <iostream>
#include <unordered_map>
#include <functional>
#include <any>
#include <typeindex>
#include <vector>
#include <memory>

// Базовый класс для событий
struct Event {
    virtual ~Event() = default;
};

// Конкретное событие
struct UserLoginEvent : public Event {
    std::string username;
    explicit UserLoginEvent(std::string name) : username(std::move(name)) {}
};

// Класс Event Bus
class EventBus {
private:
    std::unordered_map<std::type_index, std::vector<std::function<void(const std::any&)>>> handlers;
public:
    template<typename EventType>
    void subscribe(std::function<void(const EventType&)> handler) {
        auto wrapped = [handler](const std::any& event) {
            handler(std::any_cast<const EventType&>(event));
        };
        handlers[std::type_index(typeid(EventType))].push_back(wrapped);
    }
    
    template<typename EventType>
    void publish(const EventType& event) {
        auto it = handlers.find(std::type_index(typeid(EventType)));
        if (it != handlers.end()) {
            for (const auto& handler : it->second) {
                handler(std::make_any<EventType>(event));
            }
        }
    }
};

// Пример использования
int main() {
    EventBus bus;
    
    // Подписка на событие
    bus.subscribe<UserLoginEvent>([](const UserLoginEvent& event) {
        std::cout << "Пользователь вошёл в систему: " << event.username << std::endl;
    });
    
    // Публикация события
    bus.publish(UserLoginEvent("Алексей"));
    bus.publish(UserLoginEvent("Ма                    

Рекомендаций