BehavioralC++verifiedVerified
Mediator Pattern in C++
Defines an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.
How to Implement the Mediator Pattern in C++
1Step 1: Define the Mediator interface
class Mediator {
public:
virtual ~Mediator() = default;
virtual void notify(const Colleague* sender, const std::string& event) = 0;
};2Step 2: Base colleague
class Colleague {
protected:
Mediator* mediator_;
std::string name_;
public:
Colleague(Mediator* m, std::string name) : mediator_(m), name_(std::move(name)) {}
virtual ~Colleague() = default;
const std::string& name() const { return name_; }
};3Step 3: Concrete colleagues
class Button : public Colleague {
public:
using Colleague::Colleague;
void click() {
std::cout << name_ << " clicked\n";
mediator_->notify(this, "click");
}
};
class TextBox : public Colleague {
std::string text_;
public:
using Colleague::Colleague;
void setText(const std::string& t) {
text_ = t;
mediator_->notify(this, "textChanged");
}
const std::string& getText() const { return text_; }
void clear() { text_.clear(); }
};4Step 4: Concrete mediator coordinates colleagues
class FormMediator : public Mediator {
Button* submitBtn_;
TextBox* inputBox_;
public:
void setComponents(Button* btn, TextBox* box) {
submitBtn_ = btn;
inputBox_ = box;
}
void notify(const Colleague* sender, const std::string& event) override {
if (event == "click" && sender == submitBtn_) {
std::cout << "Form submitted: " << inputBox_->getText() << "\n";
inputBox_->clear();
} else if (event == "textChanged") {
std::cout << "Input changed\n";
}
}
};
int main() {
FormMediator mediator;
Button submitBtn(&mediator, "Submit");
TextBox inputBox(&mediator, "Input");
mediator.setComponents(&submitBtn, &inputBox);
inputBox.setText("Hello");
submitBtn.click();
}#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <functional>
#include <format>
#include <any>
#include <algorithm>
// [step] Define a generic event-driven mediator
class EventMediator {
struct Handler {
int id;
std::string componentId;
std::function<void(const std::string&, const std::any&)> callback;
};
std::map<std::string, std::vector<Handler>> handlers_;
int nextId_ = 0;
std::vector<std::string> log_;
public:
int on(const std::string& event, const std::string& componentId,
std::function<void(const std::string&, const std::any&)> handler) {
int id = nextId_++;
handlers_[event].push_back({id, componentId, std::move(handler)});
return id;
}
void off(int handlerId) {
for (auto& [_, handlers] : handlers_)
std::erase_if(handlers, [handlerId](const Handler& h) {
return h.id == handlerId;
});
}
void emit(const std::string& event, const std::string& sender,
const std::any& data = {}) {
log_.push_back(std::format("{} -> {}", sender, event));
auto it = handlers_.find(event);
if (it == handlers_.end()) return;
for (const auto& handler : it->second) {
if (handler.componentId != sender)
handler.callback(sender, data);
}
}
const std::vector<std::string>& auditLog() const { return log_; }
};
// [step] Component base that auto-registers with mediator
class Component {
protected:
std::string id_;
std::shared_ptr<EventMediator> mediator_;
std::vector<int> subscriptions_;
public:
Component(std::string id, std::shared_ptr<EventMediator> mediator)
: id_(std::move(id)), mediator_(std::move(mediator)) {}
virtual ~Component() {
for (int sub : subscriptions_) mediator_->off(sub);
}
const std::string& id() const { return id_; }
void subscribe(const std::string& event,
std::function<void(const std::string&, const std::any&)> handler) {
subscriptions_.push_back(mediator_->on(event, id_, std::move(handler)));
}
void publish(const std::string& event, const std::any& data = {}) {
mediator_->emit(event, id_, data);
}
};
// [step] Concrete UI components
class SearchBox : public Component {
std::string query_;
public:
using Component::Component;
void setQuery(const std::string& q) {
query_ = q;
publish("search:changed", std::any(query_));
}
void submit() { publish("search:submitted", std::any(query_)); }
};
class ResultsList : public Component {
public:
using Component::Component;
void init() {
subscribe("search:submitted", [this](const std::string& sender, const std::any& data) {
auto query = std::any_cast<std::string>(data);
std::cout << std::format("[{}] Searching for: '{}'\n", id_, query);
publish("results:loaded", std::any(std::string("3 results found")));
});
}
};
class StatusBar : public Component {
public:
using Component::Component;
void init() {
subscribe("search:changed", [this](const std::string&, const std::any& data) {
auto q = std::any_cast<std::string>(data);
std::cout << std::format("[{}] Typing: '{}'\n", id_, q);
});
subscribe("results:loaded", [this](const std::string&, const std::any& data) {
auto msg = std::any_cast<std::string>(data);
std::cout << std::format("[{}] Status: {}\n", id_, msg);
});
}
};
// [step] Wire everything together
int main() {
auto mediator = std::make_shared<EventMediator>();
SearchBox search("search-box", mediator);
ResultsList results("results-list", mediator);
StatusBar status("status-bar", mediator);
results.init();
status.init();
search.setQuery("C++ patterns");
search.submit();
std::cout << "\nAudit log:\n";
for (const auto& entry : mediator->auditLog())
std::cout << " " << entry << "\n";
}Mediator Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Mediator Pattern in the Real World
“An air traffic control tower is the classic example. Instead of every plane communicating directly with every other plane—a chaotic and dangerous mess—all aircraft talk only to the control tower. The tower mediates all interactions, directing each plane based on the overall picture it maintains. Planes are decoupled from each other entirely.”