C++实现职责链模式的基本概念
职责链模式定义
职责链模式通过将请求沿着一条链传递,直到某一个处理对象完成处理,从而实现对象之间的解耦。在该模式中,请求本身不需要知道具体处理者的实现细节,只需要沿着链路逐一传递即可。
本文以 C++实现职责链模式:让多个对象有机会处理请求的解耦设计与示例 为主题,展示如何在C++里通过多态和链式结构实现解耦和可扩展性。
设计动机与优势
解耦设计让客户端不需要筛选合适的处理对象,从而降低了耦合度,提升了系统的可维护性。通过增加新的处理器或调整链路顺序,可以在不修改客户端代码的前提下扩展系统能力。
扩展性是职责链的另一大优势:只要实现一个新的处理器并将其加入链条即可,无需改动已有处理器的实现。
设计要点与解耦原理
基于接口的可替换性
核心思想是为所有处理对象定义统一的抽象接口,使得请求在各个处理器之间传递时不依赖具体实现。通过多态接口和链式引用,链条的组合和替换成为可能。
在C++实现中,通常使用virtual方法与std::shared_ptr来管理对象生命周期,确保链路的灵活拼接和安全释放。
链路组装与传递
链路往往在初始化阶段构造,可以通过setNext方法或构造函数传入下一个处理对象。请求传递的职责在于当前处理对象是否处理成功,若不处理则继续传递给下一个节点。
使用std::shared_ptr可以避免裸指针带来的悬挂与内存泄漏问题,从而实现可维护的链路结构。
代码实现基础结构
核心接口设计
接口通常包含一个处理入口和一个指向下一个处理对象的指针。处理方法返回值用于指示是否终止链路的传播,返回false表示继续传递,返回true表示当前请求已处理完毕需要停止。
以下设计模式的要点在于保持客户端对链路细节的知情度最小化,从而实现真正的解耦。
链路的构造与传递
链路的构造可以在构造阶段完成,也可以通过外部组装策略实现动态组合。可组合的链路使得系统能灵活增删处理器,以应对不同的业务需求。
示例:简单日志处理链
需求与设计
这是一个最常见的职责链示例:将日志信息沿链路分级处理,先通过级别过滤,再将信息输出到不同的目标(如控制台、文件)。通过职责链可以轻松增加新的输出目标,而无须修改调用代码。
设计要点包括:多阶段处理、输出目标的可扩展性,以及在C++中对生命周期的安全管理。
示例代码
#include <iostream>
#include <fstream>
#include <memory>
#include <string>enum class LogLevel { DEBUG, INFO, WARNING, ERROR };struct LogRecord {LogLevel level;std::string message;std::string tag;
};class Handler {
public:virtual ~Handler() = default;void setNext(std::shared_ptr<Handler> next) { next_ = next; }void handle(const LogRecord& rec) {if (process(rec)) return;if (next_) next_->handle(rec);}
protected:virtual bool process(const LogRecord& rec) = 0; // return true to stop, false to continuestd::shared_ptr<Handler> next_;
};class LevelFilter : public Handler {
public:LevelFilter(LogLevel lvl) : minLevel_(lvl) {}
protected:bool process(const LogRecord& rec) override {if (static_cast(rec.level) < static_cast(minLevel_)) {return true; // stop processing below threshold}return false; // continue to next handler}
private:LogLevel minLevel_;
};class ConsoleLogger : public Handler {
public:ConsoleLogger(LogLevel lvl) : level_(lvl) {}
protected:bool process(const LogRecord& rec) override {if (static_cast(rec.level) >= static_cast(level_)) {std::cout << "[" << toString(rec.level) << "] "<< rec.tag << ": " << rec.message << std::endl;}return false; // always continue down the chain}
private:std::string toString(LogLevel l) const {switch (l) {case LogLevel::DEBUG: return "DEBUG";case LogLevel::INFO: return "INFO";case LogLevel::WARNING: return "WARNING";case LogLevel::ERROR: return "ERROR";}return "UNKNOWN";}LogLevel level_;
};class FileLogger : public Handler {
public:FileLogger(const std::string& filename, LogLevel lvl): filename_(filename), level_(lvl) {}
protected:bool process(const LogRecord& rec) override {if (static_cast(rec.level) >= static_cast(level_)) {std::ofstream ofs(filename_, std::ios::app);if (ofs) {ofs << "[" << toString(rec.level) << "] "<< rec.tag << ": " << rec.message << std::endl;}}return false;}
private:std::string toString(LogLevel l) const {switch (l) {case LogLevel::DEBUG: return "DEBUG";case LogLevel::INFO: return "INFO";case LogLevel::WARNING: return "WARNING";case LogLevel::ERROR: return "ERROR";}return "UNKNOWN";}std::string filename_;LogLevel level_;
};// 使用示例
int main() {auto console = std::make_shared(LogLevel::INFO);auto file = std::make_shared("app.log", LogLevel::DEBUG);console->setNext(file);LogRecord r1{LogLevel::DEBUG, "Initialization complete", "Init"};LogRecord r2{LogLevel::ERROR, "Failed to load config", "Config"};console->handle(r1); // 输出到控制台和写入文件console->handle(r2);return 0;
}
示例:事件分派的职责链
场景描述
在GUI或事件驱动系统中,事件往往需要被不同的处理器按照先后顺序进行分派。通过职责链,可以将事件分发逻辑从调用者解耦,便于扩展不同类型的事件处理器。
设计要点包括:事件类型的动态判断、处理者的组合与复用,以及对新增事件类型的无侵入扩展。
完整实现
#include <iostream>
#include <memory>
#include <string>struct Event {virtual ~Event() = default;virtual std::string type() const = 0;
};struct ClickEvent : public Event {std::string type() const override { return "Click"; }
};struct KeyEvent : public Event {std::string type() const override { return "Key"; }
};class EventHandler {
public:virtual ~EventHandler() = default;void setNext(std::shared_ptr<EventHandler> next) { next_ = next; }void handle(Event& e) {if (process(e)) return;if (next_) next_->handle(e);}
protected:virtual bool process(Event& e) = 0; // true if handledstd::shared_ptr<EventHandler> next_;
};class ClickHandler : public EventHandler {
protected:bool process(Event& e) override {if (e.type() == "Click") {std::cout << "Handled ClickEvent by ClickHandler" << std::endl;return true;}return false;}
};class KeyHandler : public EventHandler {
protected:bool process(Event& e) override {if (e.type() == "Key") {std::cout << "Handled KeyEvent by KeyHandler" << std::endl;return true;}return false;}
};// 使用示例
int main() {auto chain = std::make_shared<ClickHandler>();auto kb = std::make_shared<KeyHandler>();chain->setNext(kb);ClickEvent ce;KeyEvent ke;chain->handle(ce); // ClickHandler 处理chain->handle(ke); // KeyHandler 处理return 0;
}
性能与可维护性考虑
性能评估
职责链的主要成本来自于链路遍历的开销。在最坏情况下,请求需要经过多个处理对象才能完成处理。因此,链路长度控制与处理器的裁剪策略成为重要的优化点。
通过将高频请求尽早在链路前端被处理、对不满足条件的请求快速返回,可以显著降低延迟和资源消耗。
可维护性要点
职责链的可维护性来自于清晰的职责划分与解耦的扩展性。每个处理器应关注单一职责,修改一个处理器不应牵连其他处理器的实现。
在实际项目中,建议使用std::shared_ptr进行生命周期管理,避免悬空指针,同时提供简洁的组装接口,便于后续添加或替换处理器。
扩展:在C++中实现泛型/模板化职责链
模板化设计要点
将处理器与处理的请求类型解耦,可以使用模板来实现“任意请求类型”的职责链。模板化设计提升了代码的重用性和类型安全性。通过template<typename Req>,我们可以定义通用的处理框架。
关键点包括:统一的处理接口、对请求类型的编译时约束,以及可组合的链路。模板还能在编译期捕获更多错误,提升健壮性。
简短模板示例
#include <memory>
#include <functional>
#include <utility>template<typename Req>
class HandlerT {
public:virtual ~HandlerT() = default;void setNext(std::shared_ptr<HandlerT<Req>> next) { next_ = next; }void handle(const Req& req) {if (process(req)) return;if (next_) next_->handle(req);}
protected:virtual bool process(const Req& req) = 0;std::shared_ptr<HandlerT<Req>> next_;
};struct MyRequest {int value;
};class PrintHandler : public HandlerT {
protected:bool process(const MyRequest& req) override {// 简单示例:打印值,后续链路继续std::cout << "Value: " << req.value << std::endl;return false;}
};// 使用示例:
// auto h1 = std::make_shared<PrintHandler>();
// h1->handle(MyRequest{42});



