责任链模式

责任链模式

定义

​ 为请求创建一个接收者对象的链。该模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连接成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

举例

​ 责任链模式其实在现实生活中非常的常见,最简单的一种架构就是一个公司中对于一个员工提出的需求的处理。就比如,这个员工可能会想要加薪,可能要请假,可能想辞职。总总这些,都是员工的一些可能的行为,在提出这些需求后,员工会将这些需求提交给他的上级。就比如他的部门的老大。

​ 他部门的老大会看当前他是否存在权利能够处理当前提出的需求,就比如如果一个员工想要请个小假,这种权利一般来说一个部门老大还是有的,相反,如果员工想要的是进行升职加薪这种,那对应的部门老大这个层级是没有权利去审批这事的。那么,我们先不去考虑哪种请求会被吞的情况。

​ 一般情况下,如果一个层级的人物没有权利去处理下一级传递上来的请求,那么这个请求将会被当前任务再次提交给上级,在这种情况下,这个相对的上级还会去考虑当前自己是否有权利去处理这件事,然后做出抉择,我是需要去进行当前时间的审批处理还是我仍然没有权利去处理这件事而导致再次的往上去传递信息。这种不对的递归往上传递信息,直到信息被处理后再进行返回的结构,就是责任链模式的一种实例。

注意

​ 可以想象到,在责任链模式中,我们不应该存在一个信息没有被处理的情况,在现实中也是如此,在生活中,一个公司的最上层应该拥有着对于下面的所有请求的处理权限,在这里也是如此。当然,在责任链模式中不同的是,现实中你可能会看到上层去处理一些下层处理的是,但是在责任链模式中,这种行为是不被允许的。

​ 你可以把责任链模式看做一层层的筛网,每层筛网的大小不同,会筛去符合当前权限的事件,如果当前筛网上还存在着时间没有处理,那么这些时间就会被转移到下一层筛网进行处理,直到所有的事件都被处理。这种对于事件的处理机制,才是责任链模式的核心。

UML类图

image-20241228094455900

​ 这里使用了我看的课的一个UML类图,这个类图中的职责其实已经写的很清楚了,都看到行为性模式了你自己也应该看的懂这个类图了,这里就简单过一遍就行。

AbstractManager类

​ 这个类是我们全部责任链上的类的基类,可以看到,这个类中定义了一系列的方法来进行我们责任对象的行为规定。其中的函数我们不需要去进行详细了解,我们来抽象俩个在这个抽象类中必不可少的几个属性。

抽象成员指针

​ 在责任链模式中,对于链上的每一个元素,我们至少要提供一种能够往上去传递信息的方法,在这里我们就是通过一个成员指针来实现它的链式结构来实现的,通过这个成员指针,我们将能通过这个成员指针去递归的进行信息的处理,还是那个问题,有些操作是无法再UML类图上展示出来的,还是得等到之后的代码去进行分析。

信息处理api

​ 在每个管理层中,都应该存在能够处理下层传递的api接口,在这里面你其实也很清楚它到底是谁,这里就不再赘述。这里使用了枚举变量来进行我们处理信息的一次分类,这个无所谓,你但可以使用自己的一套数据来进行识别。

实例子类

​ 这里面的系列抽象子类其实没有什么分析的价值,在这里面,对于具体子类,其实就是继承了父类的方法并且进行了一些对于自己信息处理的特化。唯一优点讨论价值的就是它的责任链的构建,每个子类都只关注与它联系的上一层的子类的指针,对于其他的层次,子类并不关注。

对于请求发出类,这里就不再进行分析了,没意思。

代码实例

基本属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 请求类型
enum RequestType {
Leave, // 请假
Raise, // 加薪
Resignation // 辞职
};

// 请求对象
struct Request {
RequestType type;
std::string description;

Request(RequestType t, const std::string& desc)
: type(t), description(desc) {}
};

​ 这里就是一些属性的抽离定义了,没什么好说的。

抽象处理者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 抽象处理者
class Handler {
protected:
std::shared_ptr<Handler> next; // 下一个处理者

public:
virtual ~Handler() = default;

void setNext(std::shared_ptr<Handler> nextHandler) {
next = nextHandler;
}

void handleRequest(const Request& request) {
if (canHandle(request)) {
processRequest(request);
} else if (next) {
next->handleRequest(request);
} else {
std::cout << "请求未被处理:" << request.description << std::endl;
}
}

virtual bool canHandle(const Request& request) = 0;
virtual void processRequest(const Request& request) = 0;
};

​ 可以看到,在这个处理者中,我们定义了一系列的抽象方法提供使用,其他的没什么意思,主要是这个处理请求的函数。

​ 在这个处理函数中,我们会先去考虑当前是否存在权限去进行处理,如果有就直接处理并且返回。如果没有权限,那么就去递归调用责任链条中的下一层角色,直到被返回为止。

​ 理论上,一定是存在角色能够处理我们这个事件的。当然,如果你输入的是一个额外的请求,不符合我们规定的请求,那抱歉,我们将无法处理,并且将会一直返回到底。

具体处理者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 具体处理者:部门主管
class Supervisor : public Handler {
public:
bool canHandle(const Request& request) override {
return request.type == Leave;
}

void processRequest(const Request& request) override {
std::cout << "主管批准请求:" << request.description << std::endl;
}
};

// 具体处理者:部门经理
class Manager : public Handler {
public:
bool canHandle(const Request& request) override {
return request.type == Raise;
}

void processRequest(const Request& request) override {
std::cout << "经理批准请求:" << request.description << std::endl;
}
};

// 具体处理者:CEO
class CEO : public Handler {
public:
bool canHandle(const Request& request) override {
return true; // CEO可以处理所有请求
}

void processRequest(const Request& request) override {
std::cout << "CEO批准请求:" << request.description << std::endl;
}
};

​ 这一个就更没有什么好说的了,就是一些具体的处理逻辑而已。

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 主函数
int main() {
// 创建责任链
auto supervisor = std::make_shared<Supervisor>();
auto manager = std::make_shared<Manager>();
auto ceo = std::make_shared<CEO>();

supervisor->setNext(manager);
manager->setNext(ceo);

// 创建请求
Request leaveRequest(Leave, "请假一天");
Request raiseRequest(Raise, "加薪申请");
Request resignationRequest(Resignation, "辞职申请");

// 测试责任链
supervisor->handleRequest(leaveRequest);
supervisor->handleRequest(raiseRequest);
supervisor->handleRequest(resignationRequest);

return 0;
}

​ 这个也没有什么好说的,主要来看这里的日志输出

1
2
3
4
PS C:\Users\ASUS\Desktop\CS\CS_STUDY\designPattern\19-责任链模式> .\test.exe
主管批准请求:请假一天
经理批准请求:加薪申请
CEO批准请求:辞职申请

​ 可以看到,这里是正确的处理了我们提出的请求的,所以,可以看到,这种模式的作用也就在这。通过对于上层权利的不断下发,当然,上层还是保留着对应的权利的,可以实现在多个环节对于信息的处理,我们可以在各个环节中去细化信息处理的细节而不去影响其他环节上的代码。这种分包式的设计,使得各个环节上的功能更加容易扩展,同时也遵守了开放封闭原则。

归纳

责任链模式(Chain of Responsibility)是一种行为设计模式,其核心意义在于通过链式传递的方式解耦请求的发送者和处理者,从而实现系统的灵活性和可扩展性。

以下是责任链模式的具体意义:


1. 降低耦合性

  • 责任链模式通过将请求的发送者和具体处理者分离,使二者之间不再直接关联:
    • 发送者只需要将请求传递给链的起始节点,无需关心请求最终由谁处理。
    • 每个处理者只需关注自己的能力范围,并决定是否处理或将请求传递给下一个处理者。
  • 这种分离提高了系统的模块化,使得代码更易于维护。

2. 增强灵活性

  • 可以动态地调整链中的处理者以及处理顺序,而不会影响其他部分:
    • 通过新增处理者类实现新功能。
    • 通过重组链条调整优先级或职责分配。
  • 在运行时可以改变链的结构,从而适应不同的需求场景。

3. 实现职责分离

  • 每个处理者只关注自身的业务逻辑:
    • 将单一职责原则(SRP)落实到代码中,避免将多个职责耦合到一个类中。
    • 每个类的职责单一,易于测试和复用。

4. 支持请求的动态处理

  • 请求可以在责任链中

    动态传递

    ,直到某个处理者能够处理它:

    • 无需显式指定处理者,由链上的逻辑决定。
    • 这种动态决策使得请求的处理更加灵活。

5. 容错与扩展能力

  • 如果请求无法被任何处理者处理,可以通过链末端的逻辑提供默认处理方案或报错机制:
    • 避免请求被丢弃或未响应的情况。
  • 责任链模式支持扩展,例如添加一个新的处理者以处理特殊的请求,而不影响现有链条。

6. 现实中的类比

  • 责任链模式的意义也体现在现实场景中

    ,例如:

    • 公司审批流程:如请假申请,会从部门主管到经理再到CEO逐级审批。
    • 技术支持系统:用户请求会先经过一级客服,若无法处理则转给二级客服,直至请求被解决。

这种模式将复杂的职责分配抽象为“链式传递”,使流程自然且易于理解。


使用责任链模式的场景

适用场景

  1. 多个处理者可以处理同一类请求,但实际处理者不确定:
    • 如权限校验、事件分发等。
  2. 请求的处理逻辑需要解耦,避免硬编码依赖:
    • 如日志处理、UI事件处理等。
  3. 需要灵活的责任分配和动态调整:
    • 如任务分发系统、策略模式的动态实现。

不适用场景

  1. 处理链过长且复杂:
    • 过多的处理者可能导致性能下降。
  2. 请求需要明确的唯一处理者:
    • 如果明确知道请求由哪个处理者完成,则责任链模式可能不适合。

总结

责任链模式的意义在于通过链式传递,将职责分离、降低耦合,同时提高灵活性和可扩展性。它在解决复杂的职责分配问题时表现出色,尤其是在处理多层审批、事件分发等场景中,使得系统结构更清晰、更易于维护。

-------------本文结束 感谢阅读-------------