C++ Observer 패턴
객체 상태 변화를 구독자들에게 자동으로 통지하는 이벤트 시스템입니다. std::function 기반의 현대적 구현을 포함합니다.
#include <iostream>
#include <functional>
#include <unordered_map>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
// ── 이벤트 타입 정의 ──────────────────────────────────────────────────────────
enum class MarketEvent {
PriceChanged,
VolumeChanged,
MarketClosed,
};
// ── 이벤트 데이터 페이로드 ────────────────────────────────────────────────────
struct PricePayload { std::string symbol; double oldPrice; double newPrice; };
struct VolumePayload { std::string symbol; long volume; };
struct ClosePayload { std::string exchange; };
// ── 핸들러 ID (구독 취소에 사용) ──────────────────────────────────────────────
using HandlerId = std::size_t;
// ── 제네릭 이벤트 버스 ────────────────────────────────────────────────────────
template<typename EventEnum, typename... Payloads>
class EventBus {
public:
// 이벤트별 핸들러를 std::function 으로 저장
template<typename Payload>
HandlerId subscribe(EventEnum event, std::function<void(const Payload&)> handler) {
HandlerId id = nextId_++;
auto& bucket = handlers_[static_cast<int>(event)];
bucket.emplace_back(id, [h = std::move(handler)](const void* data) {
h(*static_cast<const Payload*>(data));
});
return id;
}
// 특정 핸들러 구독 취소
void unsubscribe(EventEnum event, HandlerId id) {
auto& bucket = handlers_[static_cast<int>(event)];
bucket.erase(
std::remove_if(bucket.begin(), bucket.end(),
[id](const auto& pair) { return pair.first == id; }),
bucket.end());
}
// 이벤트 발행 — 등록된 모든 핸들러에 페이로드 전달
template<typename Payload>
void publish(EventEnum event, const Payload& payload) {
auto it = handlers_.find(static_cast<int>(event));
if (it == handlers_.end()) return;
for (auto& [id, fn] : it->second)
fn(static_cast<const void*>(&payload));
}
private:
using ErasedHandler = std::pair<HandlerId, std::function<void(const void*)>>;
std::unordered_map<int, std::vector<ErasedHandler>> handlers_;
HandlerId nextId_{0};
};
// ── 주식 시장 데이터 피드 (Subject) ──────────────────────────────────────────
class StockFeed {
public:
explicit StockFeed(EventBus<MarketEvent>& bus) : bus_(bus) {}
void updatePrice(const std::string& symbol, double price) {
double old = prices_[symbol];
prices_[symbol] = price;
bus_.publish(MarketEvent::PriceChanged,
PricePayload{symbol, old, price});
}
void updateVolume(const std::string& symbol, long vol) {
bus_.publish(MarketEvent::VolumeChanged,
VolumePayload{symbol, vol});
}
void closeMarket(const std::string& exchange) {
bus_.publish(MarketEvent::MarketClosed, ClosePayload{exchange});
}
private:
EventBus<MarketEvent>& bus_;
std::unordered_map<std::string, double> prices_;
};
// ── 사용 예시 ─────────────────────────────────────────────────────────────────
int main() {
EventBus<MarketEvent> bus;
StockFeed feed(bus);
// 가격 변동 구독 — 알림 대시보드
auto dashId = bus.subscribe<PricePayload>(
MarketEvent::PriceChanged,
[](const PricePayload& p) {
std::cout << "[대시보드] " << p.symbol
<< " 가격 변동: " << p.oldPrice
<< " → " << p.newPrice << '\n';
});
// 가격 변동 구독 — 알람 시스템 (5% 이상 변동 시 경고)
bus.subscribe<PricePayload>(
MarketEvent::PriceChanged,
[](const PricePayload& p) {
if (p.oldPrice > 0) {
double pct = (p.newPrice - p.oldPrice) / p.oldPrice * 100.0;
if (std::abs(pct) >= 5.0)
std::cout << "[경보] " << p.symbol
<< " 급변동 " << pct << "%!\n";
}
});
// 거래량 구독
bus.subscribe<VolumePayload>(
MarketEvent::VolumeChanged,
[](const VolumePayload& v) {
std::cout << "[거래량] " << v.symbol
<< " : " << v.volume << "주\n";
});
// 시장 종료 구독
bus.subscribe<ClosePayload>(
MarketEvent::MarketClosed,
[](const ClosePayload& c) {
std::cout << "[종료] " << c.exchange << " 장 마감\n";
});
feed.updatePrice("AAPL", 0.0); // 초기화
feed.updatePrice("AAPL", 182.50);
feed.updatePrice("AAPL", 195.10); // 6.9% 급등 → 알람 발생
feed.updateVolume("AAPL", 45'000'000);
// 대시보드 구독 해제 후 추가 이벤트
bus.unsubscribe(MarketEvent::PriceChanged, dashId);
feed.updatePrice("AAPL", 196.00); // 알람만 수신
feed.closeMarket("NASDAQ");
}