15行为型设计模式——责任链模式
一、责任链模式简介
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,也叫职责链模式或者职责连锁模式。用于处理请求的多个对象,以实现请求的分发和处理。它的核心思想是将请求的处理职责链式地传递给多个对象中的一个或多个,从而避免请求发送者和接收者之间的耦合。该模式构造一系列分别担当不同的职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被称作责任链模式。责任链的形式在我们的日常工作和生活中处处可见。
工作、生活中的责任链模式
例1:客户Client有一个买车的需求,这个需求对客户来说就一个买车,但是对4S店或者汽车销售商来说有试驾车辆、交钱、签立合同、交接车钥匙四个部分。 首先客户Client和销售谈,客户Client试驾车辆满意后,把任务交给财务,客户Client付完余款之后,把任务交给法务,客户Client签完合同按手印之后,把任务交给车钥匙保管员,客户Client无悲无喜的拿到车钥匙走人。
例2:政府某部门的某项工作,县政府某部门先完成自己能处理的任务,不能处理的任务交给省政府某部门,省政府某部门再完成自己职责范围内的任务,不能处理的任务交给中央政府某部门,中央政府某部门最后完成该项工作的审批。
例3:某家庭分工合作缩短出游时间,某天当即出发前往明秀山川旅游,妈妈二话不说接下导游的任务,制作旅游攻略,爸爸沉默不语去了趟超市买生活用品和医用急救品,女儿贴心的备好换洗衣服和防晒霜,至于儿子负责宣传一家人出游的消息,让街坊四邻一顿羡慕+短时间内不打扰他家幸福生活。
责任链模式的结构图
主要组成部分
- 处理者(Handler):定义一个处理请求的接口,并实现具体的处理逻辑。每个处理者对象都持有对下一个处理者对象的引用。
- 具体处理者(ConcreteHandler):实现处理者接口,并具体实现请求处理的逻辑。如果无法处理请求,它将请求转发给下一个处理者。
- 客户端(Client):创建具体的处理者对象,并设置责任链的顺序。然后,向链中的第一个处理者发送请求。
工作流程
- 客户端将请求发送到责任链中的第一个处理者。
- 第一个处理者处理请求,如果不能处理,便将请求传递给下一个处理者。
- 这个过程会一直持续下去,直到请求被处理或者链中的所有处理者都没有处理请求。
责任链模式的应用场景
1. 日志记录
在日志记录系统中,通常需要将日志记录到不同的目标(如文件、控制台、网络等)。通过使用责任链模式,可以将日志记录处理分为不同的处理者,每个处理者负责不同的日志记录策略。例如,可以先将日志记录到文件中,如果写入失败,再尝试写入控制台等。
2. 权限检查
在安全系统中,权限检查通常涉及多个权限层级。责任链模式可以用来处理权限检查请求,依次检查各个权限层级,确保用户具有足够的权限来执行某个操作。
3. 消息处理
在消息处理系统中,消息可能需要经过多个处理阶段,例如解析、验证、转换和存储。使用责任链模式可以将这些处理阶段链式地组织起来,简化消息处理流程的管理。
4. 输入验证
在表单输入验证中,通常需要进行多种检查,例如格式检查、范围检查和依赖检查。责任链模式可以将这些检查逻辑分成不同的处理者,逐个处理输入验证,直到输入符合要求或所有检查都失败。
5. 任务处理
在任务处理系统中,任务可能需要经过多个步骤的处理,例如数据预处理、核心处理和结果后处理。使用责任链模式可以将这些步骤分开,实现任务的逐步处理。
6. 用户请求处理
在Web应用程序中,用户请求通常需要经过多个过滤器和处理器,例如认证、授权、请求预处理和响应后处理。责任链模式可以有效地组织这些处理步骤,确保请求按照预定的顺序得到处理。
7. 图形界面事件处理
在图形用户界面(GUI)框架中,事件处理(如鼠标点击、键盘输入)通常需要经过多个事件处理器。责任链模式可以将这些处理器按顺序组织起来,使得事件可以在处理链中被逐个处理。
8. 动态配置
在动态配置系统中,配置可能需要经过多个处理器进行解析和验证。责任链模式可以使得这些处理器按顺序处理配置项,并提供灵活的配置处理机制。
二、责任链模式的设计方法
chain.cpp
#include <iostream>
// 处理者接口
class Handler {
public:
virtual ~Handler() {}
virtual void setNext(Handler*) = 0;
virtual void handle(const std::string& request) = 0;
};
// 具体处理者:试驾车辆
class TestDriveHandler : public Handler {
public:
void setNext(Handler* next) override {
next_ = next;
}
void handle(const std::string& request) override {
if (request == "试驾车辆") {
std::cout << "这辆车试驾体验good nice!" << std::endl;
} else if (next_ != nullptr) {
next_->handle(request);
}
}
private:
Handler* next_;
};
// 具体处理者:交钱
class PaymentHandler : public Handler {
public:
void setNext(Handler* next) override {
next_ = next;
}
void handle(const std::string& request) override {
if (request == "交钱") {
std::cout << "您账户9999于某年某月某日入账亿亿宇宙币." << std::endl;
} else if (next_ != nullptr) {
next_->handle(request);
}
}
private:
Handler* next_;
};
// 具体处理者:签合同
class SignContractHandler : public Handler {
public:
void setNext(Handler* next) override {
next_ = next;
}
void handle(const std::string& request) override {
if (request == "签合同") {
std::cout << "什么年代了,还按手印啊,无语,手印按好了!" << std::endl;
} else if (next_ != nullptr) {
next_->handle(request);
}
}
private:
Handler* next_;
};
// 具体处理者:交接车钥匙
class HandoverKeyHandler : public Handler {
private:
Handler* next_;
public:
void setNext(Handler* next) override {
next_ = next;
}
void handle(const std::string& request) override {
if (request == "交接车钥匙") {
std::cout << "先生/女士,请拿好您的车钥匙" << std::endl;
} else if (next_ != nullptr) {
next_->handle(request);
}
}
};
void doWorking() {
// 设置处理链
Handler* testDrive = new TestDriveHandler;
Handler* payment = new PaymentHandler;
Handler* signContract = new SignContractHandler;
Handler* handoverKey = new HandoverKeyHandler;
testDrive->setNext(payment);
payment->setNext(signContract);
signContract->setNext(handoverKey);
// 客户请求
testDrive->handle("试驾车辆");
testDrive->handle("交钱");
testDrive->handle("签合同");
testDrive->handle("交接车钥匙");
// 释放资源
delete handoverKey;
delete signContract;
delete payment;
delete testDrive;
return ;
}
int main() {
doWorking();
return 0;
}
运行效果
三、总结
优点
- 降低耦合度:请求发送者和请求处理者之间不需要直接联系,通过责任链传递请求,解耦了发送者和处理者。
- 动态处理:可以动态地改变处理者链的顺序。
- 增强扩展性:增加新的处理者时,只需添加新处理者而不需修改现有的处理者代码。
缺点
- 不保证请求被处理:如果责任链的最后一个处理者不能处理请求,请求可能会被丢弃。
- 链的配置可能复杂:管理和配置处理者链可能会变得复杂,特别是在处理者数量较多时。
责任链模式中每一个类只需要负责自己的任务,不属于自己的任务则交给其他链上的类。链上的每一个类或组合类的相邻链之间由上一个链上的类触发下一个链上的类(抽象接口Handler帮忙)。多个链上的类组合一起最终完成一个被拆解的复杂的需求。