어떤 서비스를 위해 객체에게 '이 작업을 수행해줘'라고 요청했을 때 요청을 처리할 수 없을 때 오류나 예외 상황 발생으로 처리하거나 전달된 요청과 가장 유사한 요청 처리 방식에 따라 작업을 수행해주거나 또는 다른 객체에게 요청을 대신 처리하도록 요청하는 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);
}
이처럼 책임 연쇄 패턴은 하나의 객체가 모든 작업을 처리하는 것이 아닌 요청에 대한 처리가 제대로 이루어지지 않았을 때 다른 객체에게 요청을 넘김으로써 다른 객체에서 처리를 할 수 있게 해주는 패턴입니다.
'Programming > C++' 카테고리의 다른 글
C++ 인터프리터 패턴 (0) | 2021.06.06 |
---|---|
C++ 커맨드 패턴 (0) | 2021.05.30 |
C++ 프록시 패턴 (0) | 2021.05.29 |
C++ 플라이웨이트 패턴 (0) | 2021.05.19 |
C++ 퍼사드 패턴 (0) | 2021.05.16 |