1. 职责链模式基础回顾
职责链模式(Chain of Responsibility Pattern)是面向对象设计中的经典行为型模式之一。我第一次接触这个模式是在一个电商平台的订单处理系统中,当时需要根据不同金额的订单分配不同的审核人员。这个场景让我深刻理解了职责链的价值。
职责链模式的核心思想是:将请求的发送者和接收者解耦,让多个对象都有机会处理这个请求。这些对象连接成一条链,请求沿着链传递,直到有对象处理它为止。在C++中,典型的实现方式如下:
cpp复制class Handler {
public:
virtual ~Handler() {}
void setNext(Handler* next) { next_ = next; }
virtual void handleRequest(int request) = 0;
protected:
Handler* next_ = nullptr;
};
class ConcreteHandlerA : public Handler {
public:
void handleRequest(int request) override {
if (request < 10) {
std::cout << "HandlerA processed request " << request << std::endl;
} else if (next_) {
next_->handleRequest(request);
}
}
};
class ConcreteHandlerB : public Handler {
public:
void handleRequest(int request) override {
if (request >= 10 && request < 20) {
std::cout << "HandlerB processed request " << request << std::endl;
} else if (next_) {
next_->handleRequest(request);
}
}
};
这种基础实现有几个关键特点:
- 抽象Handler类定义了处理接口和设置后继者的方法
- 具体Handler子类实现自己的处理逻辑
- 每个Handler只需知道自己的后继者,不需要知道整个链的结构
- 请求会沿着链传递直到被处理或到达链尾
2. 职责链模式的常见变体
在实际工程中,纯经典的职责链模式往往不能满足复杂需求。经过多年项目实践,我总结了以下几种最有价值的变体实现方式。
2.1 中断式职责链
标准职责链中,请求会一直传递直到被处理。但在某些场景下,我们需要允许中间节点中断传递。例如在游戏开发中,一个攻击事件可能被多个防御系统处理,但一旦某个系统完全拦截了攻击,后续系统就不应再处理。
cpp复制class InterruptibleHandler : public Handler {
public:
bool handleRequest(int request) override {
if (canHandle(request)) {
doHandle(request);
return true; // 中断传递
}
return next_ ? next_->handleRequest(request) : false;
}
};
关键变化:
- 返回值改为bool型,true表示已处理并中断
- 调用者可以根据返回值决定后续行为
- 适合需要明确知道处理结果的场景
2.2 功能组合式职责链
在Web中间件开发中,我经常使用这种变体。每个处理器不仅可以选择处理请求,还能对请求和响应进行修改,形成处理管道。
cpp复制class Middleware {
public:
virtual void process(Request& req, Response& res, std::function<void()> next) = 0;
};
class LoggingMiddleware : public Middleware {
public:
void process(Request& req, Response& res, std::function<void()> next) override {
std::cout << "Request started: " << req.path() << std::endl;
next(); // 继续执行后续中间件
std::cout << "Response status: " << res.status() << std::endl;
}
};
特点:
- 通过next回调显式控制流程
- 可以在请求前后都执行逻辑
- 适合需要包装和装饰的场景
3. 动态职责链的实现技巧
在大型系统中,职责链的构成可能需要动态变化。我在一个金融风控系统中实现过这样的动态链,可以根据规则配置实时调整处理流程。
3.1 基于配置的链构建
cpp复制class HandlerFactory {
public:
static std::unique_ptr<Handler> createChain(const json& config) {
std::unique_ptr<Handler> head, current;
for (const auto& item : config) {
auto handler = createHandler(item["type"]);
if (!head) head = std::move(handler);
if (current) current->setNext(handler.get());
current = std::move(handler);
}
return head;
}
};
实现要点:
- 从JSON等配置文件读取链结构
- 通过工厂方法创建具体处理器
- 自动构建链式关系
- 支持运行时重新加载配置
3.2 链节点的动态增删
cpp复制class DynamicHandler : public Handler {
public:
void insertHandler(std::unique_ptr<Handler> newHandler) {
newHandler->setNext(next_);
next_ = newHandler.release();
}
void removeHandler(const std::string& type) {
Handler* curr = this;
while (curr->next_) {
if (curr->next_->type() == type) {
auto toDelete = curr->next_;
curr->next_ = toDelete->next_;
delete toDelete;
return;
}
curr = curr->next_;
}
}
};
注意事项:
- 需要处理好内存所有权
- 多线程环境下需要加锁
- 考虑链的完整性校验
4. 性能优化与高级模式
当职责链变得复杂时,性能问题就会显现。我在一个高并发网络代理项目中积累了一些优化经验。
4.1 快速失败机制
cpp复制class FastFailHandler : public Handler {
public:
void handleRequest(int request) override {
if (!preCheck(request)) {
throw std::runtime_error("Pre-check failed");
}
Handler::handleRequest(request);
}
};
优化点:
- 前置检查快速失败
- 避免不必要的链传递
- 集中式错误处理
4.2 并行化处理
在某些场景下,链中的节点可以并行处理:
cpp复制class ParallelHandler : public Handler {
public:
void handleRequest(int request) override {
std::vector<std::future<void>> futures;
for (auto& handler : handlers_) {
futures.push_back(std::async([=] { handler->handleRequest(request); }));
}
for (auto& f : futures) f.wait();
if (next_) next_->handleRequest(request);
}
private:
std::vector<std::unique_ptr<Handler>> handlers_;
};
实现要点:
- 使用std::async实现并行
- 注意线程安全和资源共享
- 最终仍需串行执行后续链
4.3 责任树模式
当简单的链式结构不能满足复杂业务时,可以考虑责任树:
cpp复制class TreeHandler : public Handler {
public:
void addBranch(std::unique_ptr<Handler> branch) {
branches_.push_back(std::move(branch));
}
void handleRequest(int request) override {
for (auto& branch : branches_) {
branch->handleRequest(request);
}
if (next_) next_->handleRequest(request);
}
private:
std::vector<std::unique_ptr<Handler>> branches_;
};
适用场景:
- 多维度条件判断
- 需要广播式处理
- 复杂业务规则
5. 实际应用案例分析
在我参与的一个电商平台优惠系统重构中,职责链模式的各种变体发挥了重要作用。
5.1 优惠券应用链
cpp复制class CouponHandler : public Handler {
public:
void handleRequest(Order& order) override {
for (auto& coupon : availableCoupons_) {
if (coupon.canApply(order)) {
coupon.apply(order);
if (coupon.isExclusive()) {
return; // 独占型优惠中断链
}
}
}
if (next_) next_->handleRequest(order);
}
};
业务特点:
- 多种优惠券类型
- 独占型和非独占型优惠
- 按优先级顺序尝试应用
5.2 风控审核链
在金融系统中,一个交易请求需要经过多重风控检查:
cpp复制class RiskCheckHandler : public Handler {
public:
bool handleRequest(Transaction& tx) override {
if (!checkAML(tx)) return false; // 反洗钱检查
if (!checkCredit(tx)) return false; // 信用检查
if (!checkLimit(tx)) return false; // 限额检查
return next_ ? next_->handleRequest(tx) : true;
}
};
关键经验:
- 任何检查失败则整个交易拒绝
- 检查顺序很重要,把代价小的放前面
- 可以动态调整检查项
6. 模式优缺点与替代方案
经过多个项目的实践,我对职责链模式有了更全面的认识。
6.1 优势总结
- 降低耦合度:请求者无需知道具体处理者
- 动态灵活:可以运行时调整处理链
- 单一职责:每个处理者只关注自己的逻辑
- 可扩展性:新增处理者不影响现有代码
6.2 局限性
- 性能开销:长链会导致处理延迟
- 调试困难:请求的流向不够透明
- 不保证处理:请求可能到达链尾未被处理
6.3 替代方案比较
-
状态模式:
- 适合状态转换明确的场景
- 状态之间耦合度更高
- 不如职责链灵活
-
命令模式:
- 将请求封装为对象
- 更强调请求的存储和延迟执行
- 可以组合成宏命令
-
观察者模式:
- 多个处理者并行接收通知
- 缺乏明确的处理顺序控制
- 适合事件广播场景
在实际项目中,我经常将这些模式组合使用。比如用职责链处理主要流程,用观察者模式通知相关方,用命令模式封装可撤销操作。
