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

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

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

Начать курс

Event Bus и паттерн Observer в C++: от теории к современной реализации

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

Паттерн 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 Subject {
private:
    std::vector<std::weak_ptr<IObserver>> observers_;
public:
    void attach(std::shared_ptr<IObserver> observer) {
        observers_.push_back(observer);
    }

    void detach(std::shared_ptr<IObserver> observer) {
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                [&observer](const std::weak_ptr<IObserver>& wp) {
                    auto sp = wp.lock();
                    return !sp || sp == observer;
                }),
            observers_.end()
        );
    }

    void notify(const std::string& message) {
        for (auto it = observers_.begin(); it != observers_.end(); ) {
            if (auto sp = it->lock()) {
                sp->update(message);
                ++it;
            } else {
                it = observers_.erase(it); // удаляем "мёртвые" ссылки
            }
        }
    }
};

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

int main() {
    auto subject = std::make_shared<Subject>();
    auto obs1 = std::make_shared<ConcreteObserver>("Наблюдатель 1");
    auto obs2 = std::make_shared<ConcreteObserver>("Наблюдатель 2");

    subject->attach(obs1);
    subject->attach(obs2);
    subject->notify("Событие произошло!");

    subject->detach(obs1);
    subject->notify("Только для второго");

    return 0;
}

Этот код работает, но у него есть ограничения: все наблюдатели должны наследовать IObserver, а сообщения — быть строками. На практике нужна более гибкая система.

Event Bus: современная эволюция

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

  • Типизированные события — каждое событие имеет свой тип данных.
  • Отсутствие жёсткой иерархии — подписчики не обязаны наследовать общий интерфейс.
  • Глобальная или локальная шина — можно иметь один Event Bus на всё приложение или несколько изолированных.

Реализация Event Bus на C++17/20 с std::any

Используем std::any для хранения данных любого типа и std::function для обработчиков. Для разделения событий по типам применим std::type_index.

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

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

// Конкретные события
struct KeyEvent : public Event {
    int keyCode;
    bool pressed;
    KeyEvent(int code, bool pr) : keyCode(code), pressed(pr) {}
};

struct MouseEvent : public Event {
    int x, y;
    MouseEvent(int px, int py) : x(px), y(py) {}
};

// Event Bus
class EventBus {
private:
    // Карта: тип события -> список обработчиков
    std::map<std::type_index, std::vector<std::function<void(const std::any&)>>> handlers_;

public:
    // Подписка на событие определённого типа
    template<typename EventType>
    void subscribe(std::function<void(const EventType&                    

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