BehavioralC++verifiedVerified
Chain of Responsibility Pattern in C++
Passes a request along a chain of handlers, where each handler decides to process it or pass it to the next handler in the chain.
How to Implement the Chain of Responsibility Pattern in C++
1Step 1: Define the Handler interface
class Handler {
protected:
std::unique_ptr<Handler> next_;
public:
virtual ~Handler() = default;
Handler* setNext(std::unique_ptr<Handler> next) {
next_ = std::move(next);
return next_.get();
}
virtual std::string handle(const std::string& request) {
if (next_) return next_->handle(request);
return "Unhandled: " + request;
}
};2Step 2: Concrete handlers
class AuthHandler : public Handler {
public:
std::string handle(const std::string& request) override {
if (request.find("auth") != std::string::npos)
return "Handled by AuthHandler";
return Handler::handle(request);
}
};
class LogHandler : public Handler {
public:
std::string handle(const std::string& request) override {
std::cout << "Log: " << request << "\n";
return Handler::handle(request);
}
};
class DataHandler : public Handler {
public:
std::string handle(const std::string& request) override {
if (request.find("data") != std::string::npos)
return "Handled by DataHandler";
return Handler::handle(request);
}
};3Step 3: Build and use the chain
int main() {
auto auth = std::make_unique<AuthHandler>();
auto log = std::make_unique<LogHandler>();
auto data = std::make_unique<DataHandler>();
auto* logPtr = auth->setNext(std::move(log));
logPtr->setNext(std::move(data));
std::cout << auth->handle("data request") << "\n";
std::cout << auth->handle("auth request") << "\n";
std::cout << auth->handle("unknown") << "\n";
}#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <format>
#include <map>
#include <chrono>
#include <any>
// [step] Define the Request/Response context
struct HttpContext {
// Request
std::string method;
std::string path;
std::map<std::string, std::string> headers;
std::string body;
// Response (mutated by handlers)
int statusCode = 200;
std::map<std::string, std::string> responseHeaders;
std::string responseBody;
// Metadata
std::map<std::string, std::any> locals;
bool halted = false;
};
// [step] Define the middleware handler type
using Middleware = std::function<void(HttpContext&, std::function<void()>)>;
// [step] Build the pipeline that chains middleware sequentially
class MiddlewarePipeline {
std::vector<std::pair<std::string, Middleware>> middlewares_;
public:
MiddlewarePipeline& use(const std::string& name, Middleware mw) {
middlewares_.emplace_back(name, std::move(mw));
return *this;
}
void execute(HttpContext& ctx) const {
size_t index = 0;
std::function<void()> next = [&]() {
if (ctx.halted || index >= middlewares_.size()) return;
auto& [name, handler] = middlewares_[index++];
handler(ctx, next);
};
next();
}
};
// [step] Concrete middleware implementations
Middleware makeLoggingMiddleware() {
return [](HttpContext& ctx, std::function<void()> next) {
auto start = std::chrono::steady_clock::now();
std::cout << std::format("--> {} {}\n", ctx.method, ctx.path);
next();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start).count();
std::cout << std::format("<-- {} {} ({}ms)\n",
ctx.statusCode, ctx.path, ms);
};
}
Middleware makeAuthMiddleware(const std::string& requiredToken) {
return [requiredToken](HttpContext& ctx, std::function<void()> next) {
auto it = ctx.headers.find("Authorization");
if (it == ctx.headers.end() || it->second != "Bearer " + requiredToken) {
ctx.statusCode = 401;
ctx.responseBody = R"({"error":"Unauthorized"})";
ctx.halted = true;
return;
}
ctx.locals["authenticated"] = true;
next();
};
}
Middleware makeRateLimitMiddleware(int maxRequests) {
auto counter = std::make_shared<int>(0);
return [counter, maxRequests](HttpContext& ctx, std::function<void()> next) {
if (++(*counter) > maxRequests) {
ctx.statusCode = 429;
ctx.responseBody = R"({"error":"Rate limit exceeded"})";
ctx.halted = true;
return;
}
next();
};
}
Middleware makeCorsMiddleware() {
return [](HttpContext& ctx, std::function<void()> next) {
ctx.responseHeaders["Access-Control-Allow-Origin"] = "*";
ctx.responseHeaders["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE";
next();
};
}
// [step] Demonstrate the middleware pipeline
int main() {
MiddlewarePipeline pipeline;
pipeline
.use("logging", makeLoggingMiddleware())
.use("cors", makeCorsMiddleware())
.use("auth", makeAuthMiddleware("secret123"))
.use("rate-limit", makeRateLimitMiddleware(100))
.use("handler", [](HttpContext& ctx, std::function<void()>) {
ctx.responseBody = std::format(R"({{"message":"Hello from {}"}})", ctx.path);
});
// Successful request
HttpContext ctx1{"GET", "/api/users", {{"Authorization", "Bearer secret123"}}, "", 200, {}, "", {}, false};
pipeline.execute(ctx1);
std::cout << "Response: " << ctx1.responseBody << "\n\n";
// Unauthorized request
HttpContext ctx2{"GET", "/api/admin", {}, "", 200, {}, "", {}, false};
pipeline.execute(ctx2);
std::cout << "Response: " << ctx2.responseBody << "\n";
}Chain of Responsibility Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Chain of Responsibility Pattern in the Real World
“Like a customer support escalation: your call starts with a front-line agent. If they can’t resolve it, they transfer you to a specialist. If the specialist can’t help, it goes to a manager. Each level either handles it or passes it up the chain.”