责任链

责任链设计模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许多个对象有机会处理请求,从而避免请求的发送者与接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

责任链模式的核心概念

  • 请求的发送者不需要知道哪个对象会处理请求。
  • 请求的接收者可以决定是否处理请求或将其传递给下一个接收者。
  • 可以动态地添加或删除责任链上的处理者(处理对象)。

结构

  1. Handler(处理者):定义一个处理请求的接口。如果处理者能够处理该请求,则进行处理,否则将请求传递给链上的下一个处理者。
  2. ConcreteHandler(具体处理者):实现处理者接口,具体处理请求的类。如果不能处理请求,则将请求转发给下一个处理者。
  3. Client(客户端):向链上的具体处理者对象提交请求。

工作流程

  1. 客户端创建一个包含多个 ConcreteHandler 对象的链。
  2. 客户端将请求发送到链中的第一个 ConcreteHandler
  3. 如果 ConcreteHandler 能处理请求,它就会进行处理。
  4. 如果 ConcreteHandler 不能处理请求,它会将请求传递给链中的下一个 ConcreteHandler
  5. 这个过程会持续到请求被处理或者到达链的末尾为止。

责任链模式的优点

  • 降低耦合度:请求的发送者和接收者解耦,便于拓展和维护。
  • 增强灵活性:在运行时可以动态地添加或删除责任链上的处理者。
  • 增加可扩展性:通过增加新的处理类来增加新的请求处理功能,不需要修改现有代码。

责任链模式的缺点

  • 请求未必会被处理:如果责任链没有正确配置,某些请求可能会得不到处理。
  • 调试困难:由于请求在多个对象间传递,调试时很难跟踪请求的传递过程。

代码示例

以下是一个简单的 C++ 示例,演示如何使用责任链模式:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <string>
#include <memory>

// Handler(处理者)接口
class Handler {
public:
virtual ~Handler() = default;
virtual void setNext(std::shared_ptr<Handler> nextHandler) = 0;
virtual void handleRequest(const std::string& request) = 0;
};

// ConcreteHandler1(具体处理者1)
class ConcreteHandler1 : public Handler {
private:
std::shared_ptr<Handler> next;

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

void handleRequest(const std::string& request) override {
if (request == "Request1") {
std::cout << "ConcreteHandler1 handled the request." << std::endl;
} else if (next) {
next->handleRequest(request);
} else {
std::cout << "No handler available for " << request << std::endl;
}
}
};

// ConcreteHandler2(具体处理者2)
class ConcreteHandler2 : public Handler {
private:
std::shared_ptr<Handler> next;

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

void handleRequest(const std::string& request) override {
if (request == "Request2") {
std::cout << "ConcreteHandler2 handled the request." << std::endl;
} else if (next) {
next->handleRequest(request);
} else {
std::cout << "No handler available for " << request << std::endl;
}
}
};

int main() {
auto handler1 = std::make_shared<ConcreteHandler1>();
auto handler2 = std::make_shared<ConcreteHandler2>();

handler1->setNext(handler2); // 设置责任链顺序

handler1->handleRequest("Request1"); // 由 ConcreteHandler1 处理
handler1->handleRequest("Request2"); // 由 ConcreteHandler2 处理
handler1->handleRequest("Request3"); // 没有处理者处理

return 0;
}

代码解释

  1. Handler 接口:定义了一个接口,包括 setNext 方法用于设置下一个处理者,以及 handleRequest 方法用于处理请求。

  2. ConcreteHandler1 和 ConcreteHandler2:具体的处理者类实现了 Handler 接口,它们检查能否处理请求,如果不能,则将请求传递给下一个处理者。

  3. 客户端代码

    • 创建两个处理者对象 handler1handler2
    • 设置 handler1 的下一个处理者为 handler2,形成一个责任链。
    • 通过 handler1 发送请求,验证它们的处理能力。

总结

责任链设计模式是一种使请求能够被多个对象处理的设计模式。这种模式使得请求的发送者与接收者解耦,并且可以根据需要动态地改变处理链的结构。使用责任链模式可以提高系统的灵活性和可扩展性,适用于需要多个对象依次处理请求的场景。

在此项目中的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
XMuxTask mux_task;
XDemuxTask demux_task;

// ... // 进行设置

// 截取5秒监控
demux_task.set_next(&mux_task);

demux_task.Start();
mux_task.Start();

MSleep(5000);

demux_task.Stop();
mux_task.Stop();

首先demux_task设置下一个责任链的节点,接着启动demux_taskmux_task线程,会调用它们的Main()函数。

mux_taskMain()函数中,会不断对pkts_自定义list进行分装。

demux_taskMain()函数中,会不断接收数据,进行解封装,并且调用Next(&pkt)函数,由于设置了demux_task.set_next(&mux_task);,因此,会调用mux_taskDo()函数,在Do()函数中,会将AVPacket数据加入pkts_中在mux_task中的Main()中进行处理。


为什么要使用智能指针?

在责任链设计模式的实现中,使用智能指针(如 std::shared_ptrstd::unique_ptr)可以带来多方面的好处,特别是在现代 C++ 编程中,智能指针是管理动态分配内存的一种安全、简洁的方式。以下是为什么在责任链中使用智能指针的主要原因:

1. 自动内存管理,防止内存泄漏

智能指针会自动管理指向对象的生命周期,避免手动管理内存可能导致的内存泄漏问题:

  • 自动释放内存:当智能指针超出其作用范围或者不再使用时,它会自动释放所管理的对象的内存。这在责任链模式中尤为重要,因为链中的每个处理者可能会在不同的时间段被创建和销毁,手动管理内存容易出错。

  • 避免内存泄漏:如果使用普通指针,程序员需要手动调用 delete 来释放内存,稍有不慎就可能忘记释放,导致内存泄漏。而智能指针会确保对象的内存在不再使用时被正确释放。

2. 简化代码,降低错误风险

智能指针减少了内存管理的复杂性,使代码更加简洁和易于维护:

  • 不需要显式调用 delete:智能指针自动处理对象的销毁工作,避免了在多个地方调用 delete 的需求,简化了代码逻辑。

  • 减少空悬指针(Dangling Pointer)风险:当一个对象被销毁后,普通指针仍然可能指向原地址,导致空悬指针。而智能指针会在对象销毁后自动将自身置为空(nullptr),降低使用已销毁对象的风险。

3. 更好的所有权管理

智能指针明确了对象的所有权关系,有助于管理责任链中对象的生命周期:

  • std::shared_ptr 的共享所有权:在责任链模式中,如果某个节点可能会被多个部分同时引用(例如同一个节点可能被多个请求链条共享使用),使用 std::shared_ptr 可以确保对象的内存管理在所有引用结束后才会被释放。

  • std::unique_ptr 的独占所有权:如果责任链的设计保证每个处理者节点只由一个父节点拥有(即没有共享所有权的情况),使用 std::unique_ptr 可以更清楚地表达这种关系,且更高效(没有引用计数的开销)。

4. 线程安全性

在多线程环境中,智能指针(如 std::shared_ptr)可以提供一定程度的线程安全性,因为它们会使用原子操作来管理引用计数。这在需要跨线程共享责任链中的处理者对象时尤其有用。

5. 便于动态构建和修改链条

责任链模式通常允许动态地添加或移除链中的处理者。使用智能指针可以简化链条的动态修改操作:

  • 插入和移除处理者:使用智能指针时,插入或移除一个处理者非常简单,因为智能指针自动管理处理者对象的内存。当某个处理者被移除后,智能指针会确保该对象(如果没有其他引用)被正确销毁。

总结

在责任链设计模式中使用智能指针是现代 C++ 的最佳实践,因为它能够:

  1. 自动管理内存,防止内存泄漏。
  2. 简化代码,降低错误风险。
  3. 提供明确的对象所有权管理。
  4. 在多线程环境中提供一定的线程安全性。
  5. 更方便地动态构建和修改责任链。

这些优势使得智能指针成为责任链模式中的一个理想选择,尤其是在需要频繁创建和销毁对象的场景中。