目录
引言
在软件开发中,设计模式是一种被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。职责链模式(Chain of Responsibility Pattern)是其中一种重要的行为型设计模式,它允许你将请求沿着处理者链进行传递,直到其中一个处理者能够处理它为止。这种模式的主要优点在于解耦请求的发送者和接收者,使系统更加灵活和可扩展。
一、职责链模式的基本概念
核心思想
职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它的核心思想是将请求的发送者和接收者解耦,通过创建一个对象链来传递请求,直到链中的某个对象能够处理该请求为止。这种模式允许多个对象都有机会处理这个请求,或者将这个请求传递给链中的下一个对象。
职责链模式结构
职责链模式主要包含以下几个角色:
-
抽象处理者(Handler):定义了一个处理请求的接口,通常包含一个抽象处理方法和一个指向下一个处理者的引用(链中的每个处理者都有一个成员变量来保存对于下一处理者的引用)。
-
具体处理者(Concrete Handler):实现了抽象处理者的处理方法,判断能否处理本次请求,如果可以则处理,否则将该请求转给它的后继者。
-
客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
UML图
应用场景
职责链模式在实际应用中有多种典型场景,以下是一些常见的应用:
- 审批流程:如报销审批、请假审批等,不同级别的审批人员构成责任链,依次处理请求。这种场景下,每个审批人员都可以选择是否批准请求,或者将其传递给下一个审批人员。
- 事件处理:在图形用户界面(GUI)开发中,事件处理机制可以采用职责链模式,将事件从用户界面传递给各种控件,以便处理用户输入。这种方式使得事件的处理更加灵活,可以动态地添加或删除事件处理器。
- 日志记录:不同级别的日志记录器可以组成责任链,根据日志级别决定是否记录日志以及如何记录。这种方式使得日志系统的配置更加灵活,可以根据需要动态地调整日志记录的级别和方式。
- 异常处理:在程序中处理异常时,可以使用职责链模式来处理不同类型的异常,以便根据异常类型采取不同的处理策略。这种方式使得异常处理更加集中和灵活,可以轻松地添加新的异常处理逻辑。
- 权限控制:在系统中控制用户访问权限时,可以使用职责链模式来构建权限控制链,根据用户的权限级别逐级检查并授权。这种方式使得权限控制更加灵活和可扩展,可以轻松地添加新的权限控制逻辑。
- HTTP请求处理:Web框架中的中间件(Middleware)可以使用职责链模式来处理HTTP请求,例如身份验证、日志记录、缓存等中间件可以依次处理请求。这种方式使得Web应用的请求处理流程更加清晰和灵活。
二、职责链模式的优点与缺点
优点
-
降低耦合度:职责链模式将请求的发送者和接收者解耦,发送者和接收者都不需要知道对方的明确信息,降低了系统的耦合度。
-
增强系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则(对扩展开放,对修改关闭)。
-
增强灵活性:可以动态地改变链内的成员或者调动它们的次序,也可以动态地新增或删除责任。
-
简化对象之间的连接:每个对象只需保持一个指向其后继者的引用,避免了使用众多的if或if-else语句。
-
职责分明:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
-
不能保证每个请求一定被处理:由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
-
性能问题:对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
-
客户端复杂性:职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
-
调试困难:当请求在链中传递时,可能难以跟踪请求的处理过程,增加了调试的难度。
三、C++实现职责链模式
以下是一个使用C++实现的职责链模式的示例,我们将通过模拟一个简单的请假审批系统来展示这个模式的应用。
定义请求类
首先,我们定义一个WrittenRequest
类,它代表一个请假请求。
#include <iostream>
#include <string>
// 定义请求类
class WrittenRequest {
public:
std::string username;
std::string content;
int day;
WrittenRequest(std::string username, std::string content, int day)
: username(username), content(content), day(day) {}
// Getters and Setters 省略
};
定义抽象处理者
然后,我们定义一个Approver
类作为抽象处理者,它包含一个指向下一个处理者的指针,并定义了处理请求的接口。
// 抽象处理者
#include <memory>
class Approver {
protected:
std::shared_ptr<Approver> successor;
std::string name;
public:
Approver(std::string name) : name(name) {}
void setSuccessor(std::shared_ptr<Approver> successor) {
this->successor = successor;
}
virtual void handle(WrittenRequest request) = 0;
};
定义具体处理者
接着,我们定义几个具体处理者类,如Director
(主任)、Manager
(经理)和GeneralManager
(总经理),它们继承自Approver
类并实现handle
方法。
// 具体处理者
class Director : public Approver {
public:
Director(std::string name) : Approver(name) {}
void handle(WrittenRequest request) override {
if (request.day < 3) {
std::cout << "[" << name << "] 审批并通过了" << std::endl;
}
else {
if (successor) {
successor->handle(request);
}
}
}
};
class Manager : public Approver {
public:
Manager(std::string name) : Approver(name) {}
void handle(WrittenRequest request) override {
if (request.day >= 3 && request.day < 10) {
std::cout << "[" << name << "] 审批并通过了" << std::endl;
}
else {
if (successor) {
successor->handle(request);
}
}
}
};
class GeneralManager : public Approver {
public:
GeneralManager(std::string name) : Approver(name) {}
void handle(WrittenRequest request) override {
if (request.day >= 10 && request.day <= 30) {
std::cout << "[" << name << "] 审批并通过了" << std::endl;
}
else {
std::cout << "请假超过30天,拒绝此申请!" << std::endl;
}
}
};
客户端代码
最后,我们在客户端创建处理者链,并发送请求。
int main() {
auto director = std::make_shared<Director>("张主任");
auto manager = std::make_shared<Manager>("李经理");
auto generalManager = std::make_shared<GeneralManager>("王总");
director->setSuccessor(manager);
manager->setSuccessor(generalManager);
WrittenRequest request("张三", "因个人原因需要请假", 15);
// 发送请求
director->handle(request);
return 0;
}
当上述程序运行时,首先会创建一个`WrittenRequest`对象,表示张三需要请假15天。然后,我们创建了三个处理者对象:`Director`(张主任)、`Manager`(李经理)和`GeneralManager`(王总),并将它们连接成一个链,其中张主任是链的起点,王总是链的终点。 在`main`函数中,我们调用了张主任的`handle`方法来处理这个请假请求。由于请假天数为15天,张主任无法直接处理(他的权限范围是小于3天),因此他会将请求传递给链中的下一个处理者——李经理。 李经理的权限范围是3天到10天,同样无法处理这个请求,因此他会继续将请求传递给王总。 王总作为链的终点,能够处理这个请求(他的权限范围是10天到30天),因此他会输出“[王总] 审批并通过了”,表示请假请求被批准。
四、总结
通过职责链模式,我们成功地将请求的发送者和接收者解耦,使得系统更加灵活和可扩展。在请假审批系统中,我们可以很容易地添加或删除处理者,而不需要修改其他处理者的代码。此外,职责链模式还允许我们动态地改变处理者的顺序,以适应不同的业务场景。 在实际应用中,职责链模式可以应用于多种场景,如日志记录、权限检查、异常处理等。通过合理地使用职责链模式,我们可以使代码更加清晰、易于维护,并提高系统的可扩展性和灵活性。