StructuralC++verifiedVerified
Bridge Pattern in C++
Decouples an abstraction from its implementation so that the two can vary independently.
How to Implement the Bridge Pattern in C++
1Step 1: Define the Implementor interface
class Renderer {
public:
virtual ~Renderer() = default;
virtual void renderCircle(double x, double y, double r) = 0;
virtual void renderRect(double x, double y, double w, double h) = 0;
};2Step 2: Concrete implementors
class SVGRenderer : public Renderer {
public:
void renderCircle(double x, double y, double r) override {
std::cout << "<circle cx='" << x << "' cy='" << y << "' r='" << r << "'/>" << "\n";
}
void renderRect(double x, double y, double w, double h) override {
std::cout << "<rect x='" << x << "' y='" << y << "' width='" << w << "' height='" << h << "'/>" << "\n";
}
};
class CanvasRenderer : public Renderer {
public:
void renderCircle(double x, double y, double r) override {
std::cout << "ctx.arc(" << x << ", " << y << ", " << r << ")\n";
}
void renderRect(double x, double y, double w, double h) override {
std::cout << "ctx.fillRect(" << x << ", " << y << ", " << w << ", " << h << ")\n";
}
};3Step 3: Abstraction that delegates to the implementor
class Shape {
protected:
std::shared_ptr<Renderer> renderer_;
public:
explicit Shape(std::shared_ptr<Renderer> renderer)
: renderer_(std::move(renderer)) {}
virtual ~Shape() = default;
virtual void draw() = 0;
};
class CircleShape : public Shape {
double x_, y_, r_;
public:
CircleShape(double x, double y, double r, std::shared_ptr<Renderer> renderer)
: Shape(std::move(renderer)), x_(x), y_(y), r_(r) {}
void draw() override { renderer_->renderCircle(x_, y_, r_); }
};
int main() {
auto svg = std::make_shared<SVGRenderer>();
auto canvas = std::make_shared<CanvasRenderer>();
CircleShape c1(10, 20, 5, svg);
CircleShape c2(10, 20, 5, canvas);
c1.draw();
c2.draw();
}#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <format>
#include <functional>
#include <map>
#include <concepts>
// [step] Define the implementor interface for message sending
class IMessageSender {
public:
virtual ~IMessageSender() = default;
virtual bool send(const std::string& to, const std::string& subject,
const std::string& body) = 0;
virtual std::string channel() const = 0;
};
// [step] Concrete implementors
class EmailSender : public IMessageSender {
std::string smtpServer_;
public:
explicit EmailSender(std::string server) : smtpServer_(std::move(server)) {}
bool send(const std::string& to, const std::string& subject,
const std::string& body) override {
std::cout << std::format("[EMAIL via {}] To: {}, Subj: {}\n",
smtpServer_, to, subject);
return true;
}
std::string channel() const override { return "email"; }
};
class SmsSender : public IMessageSender {
public:
bool send(const std::string& to, const std::string&,
const std::string& body) override {
std::cout << std::format("[SMS] To: {}, Msg: {}\n",
to, body.substr(0, 50));
return true;
}
std::string channel() const override { return "sms"; }
};
class SlackSender : public IMessageSender {
std::string webhookUrl_;
public:
explicit SlackSender(std::string url) : webhookUrl_(std::move(url)) {}
bool send(const std::string& to, const std::string& subject,
const std::string& body) override {
std::cout << std::format("[SLACK #{}] {}: {}\n", to, subject, body);
return true;
}
std::string channel() const override { return "slack"; }
};
// [step] Refined abstraction: notification types bridge to senders
class Notification {
protected:
std::shared_ptr<IMessageSender> sender_;
public:
explicit Notification(std::shared_ptr<IMessageSender> sender)
: sender_(std::move(sender)) {}
virtual ~Notification() = default;
virtual bool notify(const std::string& recipient, const std::string& message) = 0;
std::string via() const { return sender_->channel(); }
};
class UrgentNotification : public Notification {
public:
using Notification::Notification;
bool notify(const std::string& recipient, const std::string& message) override {
return sender_->send(recipient, "[URGENT] Alert",
std::format("!!! {} !!!", message));
}
};
class InfoNotification : public Notification {
public:
using Notification::Notification;
bool notify(const std::string& recipient, const std::string& message) override {
return sender_->send(recipient, "Info", message);
}
};
class DigestNotification : public Notification {
std::vector<std::string> buffer_;
public:
using Notification::Notification;
void add(const std::string& message) { buffer_.push_back(message); }
bool notify(const std::string& recipient, const std::string&) override {
std::string digest = "Digest:\n";
for (size_t i = 0; i < buffer_.size(); ++i)
digest += std::format(" {}. {}\n", i + 1, buffer_[i]);
buffer_.clear();
return sender_->send(recipient, "Daily Digest", digest);
}
};
// [step] Demonstrate mixing abstractions and implementations
int main() {
auto email = std::make_shared<EmailSender>("smtp.example.com");
auto sms = std::make_shared<SmsSender>();
auto slack = std::make_shared<SlackSender>("https://hooks.slack.com/xxx");
UrgentNotification urgentEmail(email);
urgentEmail.notify("[email protected]", "Server down!");
InfoNotification infoSms(sms);
infoSms.notify("+1234567890", "Deployment complete");
DigestNotification digestSlack(slack);
digestSlack.add("PR #42 merged");
digestSlack.add("Build passed");
digestSlack.add("3 new issues");
digestSlack.notify("dev-team", "");
std::cout << std::format("\nChannels used: {}, {}, {}\n",
urgentEmail.via(), infoSms.via(), digestSlack.via());
}Bridge Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Bridge Pattern in the Real World
“A TV remote (abstraction) works with any brand of television (implementation) because both sides communicate through an agreed IR protocol (the bridge). Samsung and LG can redesign their TVs, and universal remote makers can add new button layouts—neither side needs to know about the other’s internal design, only the shared protocol.”