c/c++ 設計模式-----職責鏈(Chain Of Responsibility)模式

白伟碧一些小心得發表於2024-06-11

一個關於漲薪審批的範例

#include <iostream>


#ifdef _DEBUG   //只在Debug(除錯)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定義new運算子
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    //薪水處理類
    class SalaryHandler
    {
    public:
        //處理加薪請求
        void raiseRequest(const string& sname, int salfigure) //引數1代表要加薪的員工名字,引數2代表要加薪多少
        {
            if (salfigure <= 1000)
            {
                //加薪要求不超過1000,部門經理可以批准
                depManagerSP(sname, salfigure);
            }
            else if (salfigure <= 5000)
            {
                //加薪要求在1000元之上但不超過5000元,技術總監才能審批
                CTOSP(sname, salfigure);
            }
            else
            {
                //加薪要求超過5000元,總經理才能審批
                genManagerSP(sname, salfigure);
            }
        }

    private:
        //部門經理審批加薪請求
        void depManagerSP(const string& sname, int salfigure)
        {
            cout << sname << "的加薪要求為:" << salfigure << "元,部門經理審批透過!" << endl;
        }

        //技術總監審批加薪請求
        void CTOSP(const string& sname, int salfigure)
        {
            cout << sname << "的加薪要求為:" << salfigure << "元,技術總監審批透過!" << endl;
        }

        //總經理審批加薪請求
        void genManagerSP(const string& sname, int salfigure)
        {
            cout << sname << "的加薪要求為:" << salfigure << "元,總經理審批透過!" << endl;
        }
    };
}

int main()
{
    _nmsp1::SalaryHandler sh;
    sh.raiseRequest("張三", 15000); //張三要求加薪1.5萬
    sh.raiseRequest("李四", 3500);  //李四要求加薪3千5
    sh.raiseRequest("王二", 800);   //王二要求加薪8百


}

職責鏈模式

namespace _nmsp2
{
    //加薪請求類
    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
    {
    public:
        //處理加薪請求
        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);
            }
        }
    };
}

int main()
{

    //(1)建立出指責鏈中包含的各個物件(部門經理、技術總監、總經理)
    _nmsp2::ParSalApprover* pzzlinkobj1 = new _nmsp2::depManager_SA();
    _nmsp2::ParSalApprover* pzzlinkobj2 = new _nmsp2::CTO_SA();
    _nmsp2::ParSalApprover* pzzlinkobj3 = new _nmsp2::genManager_SA();

    //(2)將這些物件串在一起構成職責鏈(連結串列),現在職責鏈中pzzlinkobj1排在最前面,pzzlinkobj3排在最後面。
    pzzlinkobj1->setNextChain(pzzlinkobj2);
    pzzlinkobj2->setNextChain(pzzlinkobj3);
    pzzlinkobj3->setNextChain(nullptr); //可以不寫此行,因為ParSalApprover建構函式中設定了m_nextChain為nullptr。

    //(3)建立幾位員工關於加薪的請求(物件)
    _nmsp2::RaiseRequest emp1Req("張三", 15000); //張三要求加薪1.5萬
    _nmsp2::RaiseRequest emp2Req("李四", 3500); //李四要求加薪3500
    _nmsp2::RaiseRequest emp3Req("王二", 800); //王二要求加薪800
    //看看每位員工的加薪請求由職責鏈中的哪個物件(部門經理,技術總監,總經理)來處理,從職責鏈中排在最前面的接收者pzzlinkobj1開始。
    pzzlinkobj1->processRequest(emp1Req);
    pzzlinkobj1->processRequest(emp2Req);
    pzzlinkobj1->processRequest(emp3Req);

    //(4)釋放資源
    delete pzzlinkobj1;
    delete pzzlinkobj2;
    delete pzzlinkobj3;
}
職責鏈(Chain Of Responsibility)模式
//也叫做責任鏈模式,行為型模式。----看起來與連結串列非常類似。

//(1)一個關於漲薪審批的範例
//加薪請求: <= 1000,部門經理審批。 1000 < 加薪請求 <= 5000,技術總監審批。 加薪請求 > 5000,總經理審批。

//(2)引入職責鏈(Chain Of Responsibility)模式
//定義:使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈(構成物件鏈),
//並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
//3種角色:
//a)Handler(處理者),ParSalApprover類。
//b)ConcreteHandler(具體處理者),depManager_SA,CTO_SA,genManager_SA類。
//c)Client(請求者/客戶端).

//職責鏈模式的特點:
//a)一個請求對應多個接收者,但最後只有一個接收者會處理該請求。 請求傳送者和接收者是解耦的。
//b)直線型職責鏈,可能會看到環形的或者樹形結構的職責鏈。程式執行期間可以動態的新增、修改、刪除
//職責鏈上的接收者,使針對請求的處理更具有靈活性。這是職責鏈模式的重要特色。
//c)增加新處理者不需要修改原有程式碼。符合開閉原則。
//d)如果請求傳遞到職責鏈末尾仍沒有得到處理,則應該有一個合理的預設處理方式。
//e)如果職責鏈比較長,能夠處理該請求的接收者在職責鏈中比較靠後,則可能導致請求處理的延遲。
//若需要非常快的請求處理速度,則要權衡是否使用職責鏈模式。
//f)可以分別選擇不同的接收者物件建立多條不同的職責鏈以增加接收者在職責鏈模式中的複用性。
namespace _nmsp3
{
    //敏感詞過濾器父類
    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)建立出職責鏈中包含的哥哥物件(性敏感詞過濾器,髒話詞過濾器,政治敏感詞過濾器)
    _nmsp3::ParWordFilter* pwflinkobj1 = new _nmsp3::SexyWordFilter();
    _nmsp3::ParWordFilter* pwflinkobj2 = new _nmsp3::DirtyWordFilter();
    _nmsp3::ParWordFilter* pwflinkobj3 = new _nmsp3::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;

}

//(3)單純與非單純的職責鏈模式
//單純的職責鏈模式。
//非單純的職責鏈模式 --- 功能鏈(可以被多個處理者來處理),即便一個請求未被任何處理者物件處理,也允許。
//一般用於許可權的多次多次校驗,資料的多重檢查和過濾等場合。
//範例:敏感詞過濾器。

相關文章