Programming/C++

C++ 책임 연쇄 패턴

_SYPark 2021. 5. 30. 14:32
728x90

어떤 서비스를 위해 객체에게 '이 작업을 수행해줘'라고 요청했을 때 요청을 처리할 수 없을 때 오류나 예외 상황 발생으로 처리하거나 전달된 요청과 가장 유사한 요청 처리 방식에 따라 작업을 수행해주거나 또는 다른 객체에게 요청을 대신 처리하도록 요청하는 3가지 방법이 있습니다. 위 3가지 방법 중 마지막 방법으로 처리가 된다면 사용자가 따로 개입할 필요 없이 객체의 판단에 따라 전체적으로 최적의 답을 가진 방법으로 처리를 해줄 수 있습니다.

 

사용자가 F1키를 이용해서 도움말 혹은 매뉴얼을 오픈할 때 사용자가 보고 있거나 작업 중인 기능에 따라 그에 맞는 매뉴얼의 페이지를 보여주거나 도움말을 보여주는 시스템이 있습니다. 이를 Context Sensitive 시스템이라 하는데 이 시스템이 제대로 돌아가기 위해서는 사용자의 인터페이스 객체마다 적절한 도움말 혹은 매뉴얼 페이지가 연결되어 있어야 합니다. 연결을 위해서는 적절한 기준을 가지고 연결을 시켜줘야 제대로 된 서비스와 성능 발휘가 가능합니다.

 

이를 위해서 모든 도움말을 처리하는 객체를 만들어 수행하게 하는 방법이 있습니다. F1키를 눌렀을 때 해당 UI에 지정된 도움말이 있으면 보여주고 없다면 따로 조건 처리를 해줘서 보여주게 됩니다.

하지만 위와 같은 방법은 현재 UI가 focus 되어 있는 상태들에 따라 바꿔줘야 한다는 점 등 다양한 문제에 부딪힐 수 있습니다. 

 

이렇게 모든 경우를 전담해서 하나의 객체가 담당하는 것이 아니라 문제의 구조와 문제 상에 존재하는 객체들간의 관계를 활용해서 설계를 하는 것이 바람직한 방법이라고 할 수 있습니다. 다이얼로그, 그 안의 위젯, 그 안의 버튼과 같은 구조를 가지고 하위 단에서 처리가 안되면 상위로 점점 올려가는 식으로 처리를 넘겨주게 됩니다.

class HelpHandler {
    HelpHandler(HelpHander* pObj = 0, string sHelpMsg="") {
        pSuccessor = pObj; m_HelpMsg = sHelpMsg;
    }
    virtual void SetHandler (HelpHander* pObj = 0, string sHelpMsg="") {
        pSuccessor = pObj; m_HelpMsg = sHelpMsg;
    }
    virtual void HasHelp() { return !m_HelpMsg.isEmpty(); }
    virtual void HandleHelp() {
        if ( pSuccessor ) { pSuccessor->HandleHelp(); }
    }
    virtual void ShowHelpMsg() { cout << m_HelpMsg << endl; }
protected:
    string m_HelpMsg;
private:
    HelpHandler* pSuccessor;
};

class Widget : public HelpHanlder {
protected:
    Widget(Widget* pObj = 0, string sHelpMsg="") : HelpHandler(pObj, sHelMsg) {
        pParent = pObj;
    }
private:
    Widget* pParent;
};

class Button : public Widget {
    Button(HelpHander* pObj = 0, string sHelpMsg="") : Widget(0) {
        SetHandler(pObj, sHelpMsg);
    }
    virtual void HandleHelp() {
        if (HasHelp()) { ShowHelpMsg(); }
        else { Handler::HandleHelp(); }
    }
};

int main() 
{
    Dialog* pDialog = new Dialog("Dialog Help");
    Button* pButton = new Button(pDialog);
    
    pButton->HandleHelp();
}

pButton에 대해서 HandleHelp를 요청할 때 pButton이 에러 메세지에 대한 정보를 가지고 있지 않으면 인자로 전달받은 pDialog에 HandleHelp를 요청하는 구조를 가지고 있습니다. 이처럼 어느 한 객체가 처리를 전담하지 않고 다른 객체에게 처리를 요청하도록 연계가 이루어져 있습니다.

 

만약 처리되어야 하는 요청의 종류가 여러가지일 경우 인터페이스는 하나로 가져가되 요청의 종류를 인자로 받아 그 인자에 따라 다른 처리를 해주는 방법이 있습니다.

class Request { virtual ~Request() {} };
class HelpRequest : public Request {};
class PrintRequest : public Request {};

class Handler {
    Handler(Handler* pObj) : pSuccessor(pObj) {}
    virtual void HandlerRequest(Request* pReq) {
        if(dynamic_cast<HelpRequest*>(pReq) != NULL) { }
        else if(dynamic_cast<PrintRequest*>(pReq) != NULL) { }
        else {}
    }
private:
    Handler* pSuccessor;
};

int main()
{
    Handler h(0);
    Request* pReq = new PrintRequest;
    h.HandleRequest(pReq);
}
class Request { virtual int GetKind() { return DEFAULT_REQUSET; };
class HelpRequest : public Request { virtual int GetKind() { return HELP_REQUSET; };
class PrintRequest : public Request { virtual int GetKind() { return PRINT_REQUSET; };

class Handler {
    Handler(Handler* pObj) : pSuccessor(pObj) {}
    virtual void HandlerRequest(Request* pReq) {
    	switch(pReq->GetKind())
        case HELP_REQUEST:
        case PRINT_REQUEST:
        case DEFAULT_REQUEST:
    }
private:
    Handler* pSuccessor;
};

int main()
{
    Handler h(0);
    Request* pReq = new PrintRequest;
    h.HandleRequest(pReq);
}

이처럼 책임 연쇄 패턴은 하나의 객체가 모든 작업을 처리하는 것이 아닌 요청에 대한 처리가 제대로 이루어지지 않았을 때 다른 객체에게 요청을 넘김으로써 다른 객체에서 처리를 할 수 있게 해주는 패턴입니다. 

728x90