设计模式——责任链模式

哈喽,各位盆友们!我是你们亲爱的学徒小z,今天给大家分享的文章是设计模式的------责任链模式。

定义

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

通用类图

1.具体代码

cpp 复制代码
#include <iostream>
#include <memory>

// 抽象处理者
class Handler {
public:
    virtual ~Handler() {}
    void setNext(std::shared_ptr<Handler> next) {
        this->nextHandler = next;
    }
    virtual void handleRequest(int request) = 0;

protected:
    std::shared_ptr<Handler> nextHandler;
};

// 具体处理者A
class ConcreteHandlerA : public Handler {
public:
    void handleRequest(int request) override {
        if (request >= 0 && request < 10) {
            std::cout << "ConcreteHandlerA handled request " << request << std::endl;
        } else if (nextHandler) {
            nextHandler->handleRequest(request);
        }
    }
};

// 具体处理者B
class ConcreteHandlerB : public Handler {
public:
    void handleRequest(int request) override {
        if (request >= 10 && request < 20) {
            std::cout << "ConcreteHandlerB handled request " << request << std::endl;
        } else if (nextHandler) {
            nextHandler->handleRequest(request);
        }
    }
};

// 客户端
int main() {
    auto handlerA = std::make_shared<ConcreteHandlerA>();
    auto handlerB = std::make_shared<ConcreteHandlerB>();

    handlerA->setNext(handlerB); // 设置责任链

    // 发送请求
    handlerA->handleRequest(5);
    handlerA->handleRequest(15);
    handlerA->handleRequest(25); // 这个请求将不被处理

    return 0;
}

这段代码解释了如何通过C++编程语言来实现职责链设计模式。下面是详细的步骤:

  1. 定义处理程序接口:首先,我们需要为所有处理程序定义一个公共接口。这个接口应该包括一个方法来设置下一个接收者和一个方法来处理请求。在示例中,我们有一个名为'Handler'的基类,它有两个纯虚函数:'setNext'和'handle'。
  2. 创建具体处理程序:接下来,我们实现了两个具体的处理程序:"ConcreteHandlerA"和"ConcreteHandlerB"。每个处理程序都负责处理特定范围内的请求。例如,"ConcreteHandlerA"可以处理10以内的请求,而"ConcreteHandlerB"可以处理大于10但不超过20的请求。
  3. 构建职责链:然后,我们在main函数中创建了这些处理程序的实例,并将其连接成一个链条。这样当一个请求到来时,它会从一个处理程序传递到下一个,直到找到能够处理它的处理程序为止。
  4. 发出请求:最后,我们从链的开始处发出一系列请求。根据请求值的不同,相应的处理程序会被激活并进行处理。

2.责任链模式的组成:

  • 抽象处理者(Handler):定义一个处理请求的接口,通常包含一个方法来设定下一个处理者以及一个处理请求的方法。

  • 具体处理者(ConcreteHandler):实现抽象处理者的处理方法,判断能否处理该请求,如果可以则处理,否则传递给链中的下一个处理者。

  • 客户端(Client):创建处理者对象链,并将请求发送给链的第一个处理者。

3.责任链模式的优点:

  • 降低耦合度:请求的发送者和接收者之间解耦,不再明确知道对方的身份。

  • 增强指派职责的灵活性:可以在运行时动态添加或删除(通过设置client中的nextHander)责任链中的处理者。

  • 增强对象的复用性:处理者可以复用,无需知道链的结构

    cpp 复制代码
    // 创建另一条责任链,复用已有的处理者
    auto handlerA2 = std::make_shared<ConcreteHandlerA>();
    auto handlerB2 = std::make_shared<ConcreteHandlerB>();
    
    handlerA2->setNext(handlerB2); // 新的责任链,但复用了处理者A和B

4.责任链模式的缺点:

  • 请求可能最终不被处理:如果链中的处理者没有处理请求,且没有正确地设置下一个处理者,那么请求将得不到处理。

  • 责任链太长时,可能影响性能:每个请求从链头遍历到链尾,如果处理者较多,可能影响性能。

实际应用:

  • 在实际应用中,一般会有一个封装类对责任模式进行封装,也就是替代Client类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模 块的调用,减少模块间的耦合,提高系统的灵活性。

    cpp 复制代码
    // 封装类,用于创建和配置责任链
    class HandlerChain {
    private:
        std::shared_ptr<Handler> firstHandler;
    
    public:
        HandlerChain() {
            // 在这里配置责任链
            auto handlerA = std::make_shared<ConcreteHandlerA>();
            auto handlerB = std::make_shared<ConcreteHandlerB>();
    
            handlerA->setNext(handlerB);
            firstHandler = handlerA;
        }
    
        // 提供一个方法来获取责任链的第一个处理者
        std::shared_ptr<Handler> getFirstHandler() {
            return firstHandler;
        }
    };
    
    int main() {
        // 使用封装类来获取责任链的第一个处理者
        HandlerChain handlerChain;
        auto firstHandler = handlerChain.getFirstHandler();
    
        // 发送请求
        firstHandler->handleRequest(5);
        firstHandler->handleRequest(15);
        firstHandler->handleRequest(25); // 这个请求将不被处理
    
        return 0;
    }
  • 注意事项:

    • 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个 最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免 无意识地破坏系统性能
相关推荐
汉汉汉汉汉37 分钟前
C++11新特性详解:从列表初始化到线程库
c++
楼田莉子2 小时前
C++算法题目分享:二叉搜索树相关的习题
数据结构·c++·学习·算法·leetcode·面试
大锦终2 小时前
【算法】模拟专题
c++·算法
whitepure3 小时前
万字详解Java中的面向对象(二)——设计模式
java·设计模式
方传旺3 小时前
C++17 std::optional 深拷贝 vs 引用:unordered_map 查询大对象性能对比
c++
Dontla3 小时前
Makefile介绍(Makefile教程)(C/C++编译构建、自动化构建工具)
c语言·c++·自动化
何妨重温wdys4 小时前
矩阵链相乘的最少乘法次数(动态规划解法)
c++·算法·矩阵·动态规划
重启的码农4 小时前
ggml 介绍 (6) 后端 (ggml_backend)
c++·人工智能·神经网络
重启的码农4 小时前
ggml介绍 (7)后端缓冲区 (ggml_backend_buffer)
c++·人工智能·神经网络
雨落倾城夏未凉4 小时前
5.通过拷贝构造函数复制一个对象,假如对象的成员中有个指针类型的变量,如何避免拷贝出来的副本中的该成员之下行同一块内存(等价于默认拷贝构造函数有没有缺点)
c++·后端