C++ 设计模式——职责链模式

C++ 设计模式——职责链模式

职责链(Chain Of Responsibility)模式也叫责任链模式,是一种行为型模式,用于将一个请求传递给一个链中的若干对象,哪个对象适合处理这个请求就让哪个对象来处理。职责链看起来与传统数据结构中的“链表”非常类似。

引人“职责链”设计模式的定义(实现意图):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链(构成对象链),并沿着这条链传递该请求,直到有一个对象处理它为止。

1. 主要组成成分

  1. 处理器接口(Handler Interface):定义了一个处理请求的接口,通常包含一个方法用于接收或处理请求。这个接口不确定请求的具体处理者。
  2. 具体处理器(Concrete Handlers):实现处理器接口的类。每个处理器会对请求做出处理或者将请求传递给链中的下一个处理者。具体处理器决定了处理请求的条件和方式。
  3. 请求类(Request):通常是包含多个属性的类,这些属性决定了哪个具体处理器应该处理该请求。
  4. 客户端(Client):负责创建处理链,并向链的第一个处理器发送请求。客户端不关心请求如何以及由谁处理,这样做实现了发送者和接收者之间的解耦。

2. 逐步构建职责链模式

以下示例展示了如何实现一个用于处理加薪请求的职责链模式。此示例包含三个不同级别的审批者:部门经理、技术总监和总经理,各自能够处理特定范围的加薪请求。以下是构建职责链模式的详细步骤:

步骤1:定义处理者接口

已定义 RaiseRequest 类封装了加薪请求,其中包括员工姓名和加薪金额。

//加薪请求类
class RaiseRequest
{
public:
    //构造函数
    RaiseRequest(const string& sname, int salfigure) :m_sname(sname), m_isalfigure(salfigure) {}

    //获取请求加薪的人员名字
    const string& getName() const
    {
        return m_sname;
    }
    //获取请求加薪的数字
    int getSalFigure() const
    {
        return m_isalfigure;
    }

private:
    string m_sname;      //请求加薪的人员名字
    int    m_isalfigure; //请求加薪的数字
};
步骤2:定义抽象处理者

定义了一个名为 ParSalApprover 的抽象基类,其中包含一个处理请求的纯虚函数 processRequest 和一个函数 setNextChain 用于设置链中的下一个处理者。

//薪水审批者父类
class ParSalApprover
{
public:
    ParSalApprover() :m_nextChain(nullptr) {}
    virtual ~ParSalApprover() {} //做父类时析构函数应该为虚函数

    //设置指向的职责链中的下个审批者
    void setNextChain(ParSalApprover* next)
    {
        m_nextChain = next;
    }

    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req) = 0;

protected:
    //找链中的下个对象并把请求投递给下个链中的对象
    void sendRequestToNextHandler(const RaiseRequest& req)
    {
        //找链中的下个对象
        if (m_nextChain != nullptr)
        {
            //把请求投递给链中的下个对象
            m_nextChain->processRequest(req);
        }
        else
        {
            //没找到链中的下个对象,程序流程执行这里似乎不应该
            cout << req.getName() << "的加薪要求为:" << req.getSalFigure() << "元,但无人能够审批!" << endl;
        }
    }

private:
    ParSalApprover* m_nextChain; //指向下一个审批者(对象)的多态指针(指向自身类型),每个都指向下一个,就会构成一个职责链(链表)
};
步骤3: 创建具体处理者

创建了三个具体处理者:depManager_SA, CTO_SA, 和 genManager_SA,每个类根据其规则处理请求。

//部门经理子类
class depManager_SA :public ParSalApprover
{
public:
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure <= 1000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,部门经理审批通过!" << endl;
        }
        else
        {
            //自己不能处理,尝试找链中的下个对象来处理
            sendRequestToNextHandler(req);
        }
    }
};

//技术总监子类
class CTO_SA :public ParSalApprover
{
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure > 1000 && salfigure <= 5000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,技术总监审批通过!" << endl;
        }
        else
        {
            sendRequestToNextHandler(req);//自己不能处理,尝试找链中的下个对象来处理
        }
    }
};

//总经理子类
class genManager_SA :public ParSalApprover
{
public:
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure > 5000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,总经理审批通过!" << endl;
        }
        else
        {
            sendRequestToNextHandler(req);//自己不能处理,尝试找链中的下个对象来处理
        }
    }
};
步骤4: 配置职责链

在主函数中,创建处理者对象,并按逻辑顺序将它们连接起来形成一个链。

int main()
{
    //(1)创建出职责链中包含的各个对象(部门经理、技术总监、总经理)
    ParSalApprover* pzzlinkobj1 = new depManager_SA();
    ParSalApprover* pzzlinkobj2 = new CTO_SA();
    ParSalApprover* pzzlinkobj3 = new genManager_SA();
    //(2)将这些对象串在一起构成职责链(链表),现在职责链中pzzlinkobj1排在最前面,pzzlinkobj3排在最后面
    pzzlinkobj1->setNextChain(pzzlinkobj2);
    pzzlinkobj2->setNextChain(pzzlinkobj3);
    pzzlinkobj3->setNextChain(nullptr); //可以不写此行,因为ParSalApprover构造函数中设置了m_nextChain为nullptr

    //(3)创建几位员工关于加薪的请求(对象)
    RaiseRequest emp1Req("张三", 15000);//张三要求加薪1.5万
    RaiseRequest emp2Req("李四", 3500);//李四要求加薪3千5
    RaiseRequest emp3Req("王二", 800);//王二要求加薪8百
    //看看每位员工的加薪请求由职责链中的哪个对象(部门经理、技术总监、总经理)来处理,从职责链中排在最前面的接收者(pzzlinkobj1)开始
    pzzlinkobj1->processRequest(emp1Req);
    pzzlinkobj1->processRequest(emp2Req);
    pzzlinkobj1->processRequest(emp3Req);

    //(4)释放资源
    delete pzzlinkobj1;
    delete pzzlinkobj2;
    delete pzzlinkobj3;

    return 0;
}

3. 备忘录模式 UML 图

职责链模式 UML 图

UML 图解析

职责链模式的 UML 图中包含3种角色:

  1. Handler (处理者):
    • 作用:定义处理请求的接口(通常为 processRequest),同时也记录了链中下一个处理者的引用(在本例中使用 m_nextChain 来记录)。
    • 实现:在示例中,ParSalApprover 类扮演此角色,提供了设置下一个处理者和处理请求的基本框架。
  2. ConcreteHandler (具体处理者):
    • 作用:实现具体请求的处理逻辑。如果当前处理者无法完全处理请求,则将请求传递给链中的下一个处理者(后继者)。
    • 实现:在示例中,depManager_SACTO_SAgenManager_SA 类是具体处理者。每个类都有条件判断是否能处理请求,不能处理时将请求传递给链上的下一个处理者。
  3. Client (请求者/客户端):
    • 作用:向职责链上的具体处理者对象提交处理请求。客户端负责初始化处理链并触发请求处理过程。
    • 实现:在示例中,主函数 main 执行了客户端的角色。除了提交处理请求,它还负责创建职责链对象并搭建职责链。这部分通常是由客户端完成,因为职责链的结构和顺序可能根据不同的业务场景需要不同的配置。

4. 单纯与非单纯的职责链模式

在设计模式中,职责链模式可以分为两种形式:单纯的职责链模式和非单纯的职责链模式。这两种形式主要区别在于请求在链中的传递方式和处理者的责任范围。非单纯的职责链模式中允许一个请求被某个接收者处理后继续沿着职责链传递,其他处理者仍有机会继续处理该请求,这样的职责链往往也被称为功能链,即便一个请求未被任何处理者对象处理,在非单纯的职责链模式中也是允许的。功能链一般用于权限的多次多重校验、数据的多重检查和过滤等场合。

下面给出这个敏感词过滤器非单纯职责链模式范例(参照前面的范例书写即可)。该过滤器能够把聊天内容中涉及性、脏话、政治内容的关键词寻找出来并用一些其他符号来代替。首先创建敏感词过滤器父类,代码如下:

4.1 敏感词过滤器父类

定义一个抽象基类,其中包含将请求传递给链中下一个处理者的方法。

//敏感词过滤器父类
class ParWordFilter
{
public:
    ParWordFilter() :m_nextChain(nullptr) {}
    virtual ~ParWordFilter() {} //做父类时析构函数应该为虚函数

    //设置指向的职责链中的下个过滤器
    void setNextChain(ParWordFilter* next)
    {
        m_nextChain = next;
    }

    //处理敏感词过滤请求
    virtual string processRequest(string strWord) = 0;

protected:
    //找链中的下个对象并把请求投递给下个链中的对象
    string sendRequestToNextHandler(string strWord)
    {
        //找链中的下个对象
        if (m_nextChain != nullptr)
        {
            //把请求投递给链中的下个对象
            return m_nextChain->processRequest(strWord);
        }
        return strWord;
    }
private:
    ParWordFilter* m_nextChain;
};
4.2 具体过滤器实现

接着,创建敏感词过滤器子类共3个,分别用于过滤性、脏话、政治内容,代码如下:

//性敏感词过滤器子类
class SexyWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"性\"敏感词并用XXX来替换!" << endl;
        strWord += "XXX"; //测试代码,具体的实现逻辑略......
        return sendRequestToNextHandler(strWord);
    }
};

//脏话词过滤器子类
class DirtyWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"脏话\"敏感词并用YYY来替换!" << endl;
        strWord += "YYY";
        return sendRequestToNextHandler(strWord);
    }
};

//政治敏感词过滤器子类
class PoliticsWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"政治\"敏感词并用ZZZ来替换!" << endl;
        strWord += "ZZZ";
        return sendRequestToNextHandler(strWord);
    }
};
4.3 主函数

在主函数中创建和配置职责链。

int main()
{  
    //(1)创建出职责链中包含的各个对象(性敏感词过滤器、脏话词过滤器、政治敏感词过滤器)
    ParWordFilter* pwflinkobj1 = new SexyWordFilter();
    ParWordFilter* pwflinkobj2 = new DirtyWordFilter();
    ParWordFilter* pwflinkobj3 = new PoliticsWordFilter();
    //(2)将这些对象串在一起构成职责链(链表),现在职责链中pwflinkobj1排在最前面,pwflinkobj3排在最后面
    pwflinkobj1->setNextChain(pwflinkobj2);
    pwflinkobj2->setNextChain(pwflinkobj3);
    pwflinkobj3->setNextChain(nullptr);
    string strWordFilterResult = pwflinkobj1->processRequest("你好,这里是过滤敏感词测试范例"); //从职责链中排在最前面的接收者(pwflinkobj1)开始,processRequest的参数代表的是聊天内容
    cout << "对敏感词过滤后的结果为:" << strWordFilterResult << endl;
    //(3)释放资源
    delete pwflinkobj1;
    delete pwflinkobj2;
    delete pwflinkobj3;
    
    return 0;
}

执行结果:

通过与词库比对,在strWord中查找"性"敏感词并用XXX来替换!
通过与词库比对,在strWord中查找"脏话"敏感词并用YYY来替换!
通过与词库比对,在strWord中查找"政治"敏感词并用ZZZ来替换!
对敏感词过滤后的结果为:你好,这里是过滤敏感词测试范例XXXYYYZZZ

从结果可以看到,聊天内容(请求)先经过性敏感词过滤器处理,然后再把处理后的聊天内容(请求)传递给脏话敏感词过滤器,脏话敏感词过滤器处理完后再传递给政治敏感词过滤器,这些过滤器形成了一个链条。链条上的每个过滤器各自承担自己的处理职责,经过多次被处理并被放行传递到下一个过滤器的过程,最终处理结果被返回到strWordFilterResult中。

5. 职责链模式的优点

  • 减少请求的发送者和接收者之间的耦合:发送者和接收者不需要知道彼此的具体细节。
  • 增强了系统的灵活性:可以动态地添加或修改处理链,而不影响其他部分。
  • 增强了给定请求的处理机会:请求可以被多个对象处理,不必绑定到特定的处理者。

6. 职责链模式的缺点

  • 请求可能未被处理:如果链配置不当,请求可能会到达链的末端而未被处理。
  • 性能问题:在某些情况下,由于需要在链上进行多次处理判断,可能会引入性能瓶颈。
  • 调试困难:由于处理过程的动态性,可能难以跟踪请求在链中的传递路径。

7. 职责链模式的适用场景

  1. 多个对象可以处理同一请求:当多个对象都可能对请求进行处理,但具体由哪个对象处理在运行时才确定时,职责链模式提供了一种灵活的处理方式。
  2. 不明确具体接收者:在请求的发送者不需要知道请求的具体接收者是谁的情况下,使用职责链模式可以将发送者和接收者解耦。
  3. 可动态指定处理的顺序:当需要在运行时动态改变请求处理者的顺序或者动态添加新的处理者时,职责链模式能够提供这种灵活性。

总结

职责链模式通过建立一条对象链来处理请求,实现了请求发送者和接收者之间的解耦。它在处理具有不同处理级别的请求时特别有用,如日志记录、异常处理等场景。然而,使用时需要注意避免请求在链中过度传递,以及确保所有请求都能得到适当处理。

完整代码

#include <iostream>
using namespace std;

//加薪请求类
class RaiseRequest
{
public:
    //构造函数
    RaiseRequest(const string& sname, int salfigure) :m_sname(sname), m_isalfigure(salfigure) {}

    //获取请求加薪的人员名字
    const string& getName() const
    {
        return m_sname;
    }
    //获取请求加薪的数字
    int getSalFigure() const
    {
        return m_isalfigure;
    }

private:
    string m_sname;      //请求加薪的人员名字
    int    m_isalfigure; //请求加薪的数字
};

//-------------------
//薪水审批者父类
class ParSalApprover
{
public:
    ParSalApprover() :m_nextChain(nullptr) {}
    virtual ~ParSalApprover() {} //做父类时析构函数应该为虚函数

    //设置指向的职责链中的下个审批者
    void setNextChain(ParSalApprover* next)
    {
        m_nextChain = next;
    }

    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req) = 0;

protected:
    //找链中的下个对象并把请求投递给下个链中的对象
    void sendRequestToNextHandler(const RaiseRequest& req)
    {
        //找链中的下个对象
        if (m_nextChain != nullptr)
        {
            //把请求投递给链中的下个对象
            m_nextChain->processRequest(req);
        }
        else
        {
            //没找到链中的下个对象,程序流程执行这里似乎不应该
            cout << req.getName() << "的加薪要求为:" << req.getSalFigure() << "元,但无人能够审批!" << endl;
        }
    }

private:
    ParSalApprover* m_nextChain; //指向下一个审批者(对象)的多态指针(指向自身类型),每个都指向下一个,就会构成一个职责链(链表)
};

//部门经理子类
class depManager_SA :public ParSalApprover
{
public:
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure <= 1000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,部门经理审批通过!" << endl;
        }
        else
        {
            //自己不能处理,尝试找链中的下个对象来处理
            sendRequestToNextHandler(req);
        }
    }
};

//技术总监子类
class CTO_SA :public ParSalApprover
{
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure > 1000 && salfigure <= 5000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,技术总监审批通过!" << endl;
        }
        else
        {
            sendRequestToNextHandler(req);//自己不能处理,尝试找链中的下个对象来处理
        }
    }
};

//总经理子类
class genManager_SA :public ParSalApprover
{
public:
    //处理加薪请求
    virtual void processRequest(const RaiseRequest& req)
    {
        int salfigure = req.getSalFigure();
        if (salfigure > 5000)
        {
            //如果自己能处理,则自己处理
            cout << req.getName() << "的加薪要求为:" << salfigure << "元,总经理审批通过!" << endl;
        }
        else
        {
            sendRequestToNextHandler(req);//自己不能处理,尝试找链中的下个对象来处理
        }
    }
};

//敏感词过滤器父类
class ParWordFilter
{
public:
    ParWordFilter() :m_nextChain(nullptr) {}
    virtual ~ParWordFilter() {} //做父类时析构函数应该为虚函数

    //设置指向的职责链中的下个过滤器
    void setNextChain(ParWordFilter* next)
    {
        m_nextChain = next;
    }

    //处理敏感词过滤请求
    virtual string processRequest(string strWord) = 0;

protected:
    //找链中的下个对象并把请求投递给下个链中的对象
    string sendRequestToNextHandler(string strWord)
    {
        //找链中的下个对象
        if (m_nextChain != nullptr)
        {
            //把请求投递给链中的下个对象
            return m_nextChain->processRequest(strWord);
        }
        return strWord;
    }
private:
    ParWordFilter* m_nextChain;
};

//性敏感词过滤器子类
class SexyWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"性\"敏感词并用XXX来替换!" << endl;
        strWord += "XXX"; //测试代码,具体的实现逻辑略......
        return sendRequestToNextHandler(strWord);
    }
};

//脏话词过滤器子类
class DirtyWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"脏话\"敏感词并用YYY来替换!" << endl;
        strWord += "YYY";
        return sendRequestToNextHandler(strWord);
    }
};

//政治敏感词过滤器子类
class PoliticsWordFilter :public ParWordFilter
{
public:
    virtual string processRequest(string strWord)
    {
        cout << "通过与词库比对,在strWord中查找\"政治\"敏感词并用ZZZ来替换!" << endl;
        strWord += "ZZZ";
        return sendRequestToNextHandler(strWord);
    }
};

int main()
{

    //(1)创建出职责链中包含的各个对象(部门经理、技术总监、总经理)
    ParSalApprover* pzzlinkobj1 = new depManager_SA();
    ParSalApprover* pzzlinkobj2 = new CTO_SA();
    ParSalApprover* pzzlinkobj3 = new genManager_SA();
    //(2)将这些对象串在一起构成职责链(链表),现在职责链中pzzlinkobj1排在最前面,pzzlinkobj3排在最后面
    pzzlinkobj1->setNextChain(pzzlinkobj2);
    pzzlinkobj2->setNextChain(pzzlinkobj3);
    pzzlinkobj3->setNextChain(nullptr); //可以不写此行,因为ParSalApprover构造函数中设置了m_nextChain为nullptr

    //(3)创建几位员工关于加薪的请求(对象)
    RaiseRequest emp1Req("张三", 15000);//张三要求加薪1.5万
    RaiseRequest emp2Req("李四", 3500);//李四要求加薪3千5
    RaiseRequest emp3Req("王二", 800);//王二要求加薪8百
    //看看每位员工的加薪请求由职责链中的哪个对象(部门经理、技术总监、总经理)来处理,从职责链中排在最前面的接收者(pzzlinkobj1)开始
    pzzlinkobj1->processRequest(emp1Req);
    pzzlinkobj1->processRequest(emp2Req);
    pzzlinkobj1->processRequest(emp3Req);

    //(4)释放资源
    delete pzzlinkobj1;
    delete pzzlinkobj2;
    delete pzzlinkobj3;


    //(1)创建出职责链中包含的各个对象(性敏感词过滤器、脏话词过滤器、政治敏感词过滤器)
    ParWordFilter* pwflinkobj1 = new SexyWordFilter();
    ParWordFilter* pwflinkobj2 = new DirtyWordFilter();
    ParWordFilter* pwflinkobj3 = new PoliticsWordFilter();

    //(2)将这些对象串在一起构成职责链(链表),现在职责链中pwflinkobj1排在最前面,pwflinkobj3排在最后面
    pwflinkobj1->setNextChain(pwflinkobj2);
    pwflinkobj2->setNextChain(pwflinkobj3);
    pwflinkobj3->setNextChain(nullptr);
    string strWordFilterResult = pwflinkobj1->processRequest("你好,这里是过滤敏感词测试范例"); //从职责链中排在最前面的接收者(pwflinkobj1)开始,processRequest的参数代表的是聊天内容
    cout << "对敏感词过滤后的结果为:" << strWordFilterResult << endl;

    //(3)释放资源
    delete pwflinkobj1;
    delete pwflinkobj2;
    delete pwflinkobj3;


    return 0;
}
  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值