设计模式:责任链模式 Chain of Responsibility

目录


前言

责任链是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。


问题

假如你正在开发一个在线订购系统。 你希望对系统访问进行限制, 只允许认证用户创建订单。 此外, 拥有管理权限的用户也拥有所有订单的完全访问权限。

简单规划后, 你会意识到这些检查必须依次进行。 只要接收到包含用户凭据的请求, 应用程序就可尝试对进入系统的用户进行认证。 但如果由于用户凭据不正确而导致认证失败,那就没有必要进行后续检查了。

在接下来的几个月里, 你实现了后续的几个检查步骤。

• 一位同事认为直接将原始数据传递给订购系统存在安全隐患。因此你新增了额外的验证步骤来清理请求中的数据。

• 过了一段时间, 有人注意到系统无法抵御暴力密码破解方式的攻击。 为了防范这种情况, 你立刻添加了一个检查步骤来过滤来自同一 IP 地址的重复错误请求。

• 又有人提议你可以对包含同样数据的重复请求返回缓存中的结果, 从而提高系统响应速度。 因此, 你新增了一个检查步骤, 确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。

检查代码本来就已经混乱不堪, 而每次新增功能都会使其更加臃肿。 修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复用这些检查步骤来保护其他系统组件时, 你只能复制部分代码, 因为这些组件只需部分而非全部的检查步骤。

系统会变得让人非常费解, 而且其维护成本也会激增。 你在艰难地和这些代码共处一段时间后, 有一天终于决定对整个系统进行重构。

解决方案

与许多其他行为设计模式一样, 责任链会将特定行为转换为被称作处理者的独立对象。 在上述示例中, 每个检查步骤都可被抽取为仅有单个方法的类, 并执行检查操作。 请求及其数据则会被作为参数传递给该方法。

模式建议你将这些处理者连成一条链。 链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。 除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动,直至所有处理者都有机会对其进行处理。

最重要的是: 处理者可以决定不再沿着链传递请求, 这可高效地取消所有后续处理步骤。

在我们的订购系统示例中, 处理者会在进行请求处理工作后决定是否继续沿着链传递请求。 如果请求中包含正确的数据,所有处理者都将执行自己的主要行为, 无论该行为是身份验证还是数据缓存。

不过还有一种稍微不同的方式(也是更经典一种), 那就是处理者接收到请求后自行决定是否能够对其进行处理。 如果自己能够处理, 处理者就不再继续传递请求。 因此在这种情况下, 每个请求要么最多有一个处理者对其进行处理, 要么没有任何处理者对其进行处理。 在处理图形用户界面元素栈中的事件时, 这种方式非常常见。

例如, 当用户点击按钮时, 按钮产生的事件将沿着GUI元素链进行传递, 最开始是按钮的容器(如窗体或面板), 直至应用程序主窗口。 链上第一个能处理该事件的元素会对其进行处理。 此外, 该例还有另一个值得我们关注的地方: 它表明我们总能从对象树中抽取出链来。

所有处理者类均实现同一接口是关键所在。 每个具体处理者仅关心下一个包含 execute 执行 方法的处理者。 这样一来, 你就可以在运行时使用不同的处理者来创建链, 而无需将相关代码与处理者的具体类进行耦合。

结构

代码

cpp 复制代码
#include <iostream>
#include <memory>
#include <string>
using namespace std;

class Handler{
public:
    virtual void setNext(shared_ptr<Handler> h) = 0;
    virtual void handle(const string& request) = 0;
};

class BaseHandler : public Handler{
public:
    void setNext(shared_ptr<Handler> h) override{
        m_next = h;
    }
    
    void handle(const string& request) override{
        if(m_next != nullptr){
            m_next->handle(request);
        }
    }
protected:
    shared_ptr<Handler> m_next;
};

// 具体处理者1:处理"100"请求
class ConcreteHandler1 : public BaseHandler{
public:
    void handle(const string& request) override{
        if(request == "来自职员的请求:给我100块"){
            cout << "处理者1:能够处理" << endl;
        } else {
            cout << "处理者1:不能处理,交给下一位" << endl;
            BaseHandler::handle(request); // 传递给下一个
        }
    }
};

// 具体处理者2:处理"给我1w块"请求
class ConcreteHandler2 : public BaseHandler{
public:
    void handle(const string& request) override{
        if(request == "来自职员的请求:给我1000块"){
            cout << "处理者2:批准1000块请求" << endl;
        } else {
            cout << "处理者2:不能处理,交给下一位" << endl;
            BaseHandler::handle(request); // 传递给下一个
        }
    }
};

// 大Boss:作为链的最后一个节点,处理所有剩余请求
class BossHandler : public BaseHandler{
public:
    void handle(const string& request) override{
                if(request == "来自职员的请求:给我10000块"){
            cout << "大Boss:批准10000块请求" << endl;
        } else {
            cout << "大Boss:没门,你被开了!!" << endl;
        }
    }
};

int main(){
    auto h1 = make_shared<ConcreteHandler1>();
    auto h2 = make_shared<ConcreteHandler2>();
    auto boss = make_shared<BossHandler>(); 
    
    h1->setNext(h2);
    h2->setNext(boss);
    
    cout << "-----------测试处理者1的决策-----------" << endl;
    h1->handle("来自职员的请求:给我100块");
    
    cout << "-----------测试处理者2的决策-----------" << endl;
    h1->handle("来自职员的请求:给我1000块");
    
    cout << "----------测试大Boss的决策----------" << endl;
    h1->handle("来自职员的请求:给我10000块"); 

    cout << "----------测试开除结局----------" << endl;
    h1->handle("来自职员的请求:给我100000块"); 
    
    return 0;
}
相关推荐
牛奶咖啡131 小时前
学习设计模式《二十三》——桥接模式
学习·设计模式·桥接模式·认识桥接模式·桥接模式的优点·何时选用桥接模式·桥接模式的使用示例
左灯右行的爱情1 小时前
深度学习设计模式:责任链(Chain of Responsibility)模式(例子+业务场景+八股)
深度学习·设计模式·责任链模式
是2的10次方啊2 小时前
🚀 Spring设计模式大揭秘:23种模式藏在你每天在用的框架里
设计模式
东北南西2 小时前
设计模式-单例模式
前端·javascript·单例模式·设计模式
快乐非自愿3 小时前
掌握设计模式--命令模式
设计模式·命令模式
我希望的一路生花12 小时前
Nik Collection 6.2全新版Nik降噪锐化调色PS/LR插件
人工智能·计算机视觉·设计模式·stable diffusion·aigc
东北南西18 小时前
设计模式-工厂模式
前端·设计模式
JohnYan19 小时前
工作笔记 - 改进的单例应用
javascript·设计模式·bun
郝学胜-神的一滴21 小时前
使用C++11改进工厂方法模式:支持运行时配置的增强实现
开发语言·c++·程序人生·设计模式
Leo来编程21 小时前
设计模式3-模板方法模式
设计模式·模板方法模式