Thinking Different







Chain of Responsibility - 책임 연쇄 패턴


  • 요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여함으로써 요청하는 객체와 처리하는 객체 사이의 결합도를 없애는 패턴
  • 요청을 해결할 객체를 만날 때까지 객체 고리를 따라서 요청을 전달한다.
  • 일반적으로 처리할 수 있는 모든 객체들을 고리고 묶고 맨 처음 객체에게 요청을 전달하여 처리하지 못하면 다음 객체로 넘기는 방식(선처리 후 책임 전가)으로 진행된다
  • 연결리스트라고 생각하면 이해가 쉽다.







샘플 코드)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//------------------------------------------------------------------
// Handler 인터페이스 클래스
class Handler
{
public:
    Handler(Handler* pHandle) : pHandler(pHandle) {}
    ~Handler() { if (pHandler) delete pHandler; }
 
public:
    virtual void handleRequest(int i)
    {
        if (pHandler != NULL)
            pHandler->handleRequest(i);
    }
 
private:
    Handler* pHandler;
};
 
//------------------------------------------------------------------
// ConcreteHandler1 상속 클래스
class ConcreteHandler1 : public Handler
{
public:
    ConcreteHandler1(Handler* pHandle) : Handler(pHandle) {}
 
public:
    void handleRequest(int i) override
    {
        if (i % 2 == 0)
            cout << "짝수 입니다" << endl;
        else
            Handler::handleRequest(i);
    }
};
 
//------------------------------------------------------------------
// ConcreteHandler2 상속 클래스
class ConcreteHandler2 : public Handler
{
public:
    ConcreteHandler2(Handler* pHandle) : Handler(pHandle) {}
 
public:
    void handleRequest(int i) override 
    {
        if (i % 2 != 0)
            cout << "홀수 입니다" << endl;
        else
            Handler::handleRequest(i);
    }
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    Handler* pHandler = new ConcreteHandler1(new ConcreteHandler2(NULL));
 
    pHandler->handleRequest(25);
 
    delete pHandler;
 
    return 0;
}





예제를 통한 책임 연쇄 패턴(Chain of Responsibility Pattern) 알아보기



예제) 클래스 레벨에 따른 상담 문의 예제



문의자 클래스가 있고, 각각의 3단계(초,중,고)의 레벨을 가진다. 상담문의 클래스는 각각 레벨에 따른 상담 클래스를 만들고 채인 구조로 설정한다. 최초 문의시 초등상담부터 차례대로 돌아가면서 레벨에 맞는 상담을 시작한다.

여기에서는 RTTI Dynamic_cast를 활용한 소스를 추가하였습니다.



예제 소스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//------------------------------------------------------------------
// 문의자 인터페이스 클래스
class 문의자 
{
public:
    virtual const TCHAR* Class() = 0;
};
class 초등학생 : public 문의자
{
public:
    const TCHAR* Class() { return "초등학생"; }
};
class 중학생 : public 문의자 
{
public:
    const TCHAR* Class() { return "중학생"; }
};
class 고등학생 : public 문의자 
{
public:
    const TCHAR* Class() { return "고등학생"; }
};
 
//------------------------------------------------------------------
// 수학상담문의 인터페이스 클래스
class 수학상담문의
{
public:
    수학상담문의(수학상담문의* pHandle) : pHandler(pHandle) {}
    ~수학상담문의() { if (pHandler) delete pHandler; }
 
public:
    virtual void 상담문의(문의자* pUser)
    {
        if (pHandler != NULL)
            pHandler->상담문의(pUser);
    }
 
private:
    수학상담문의* pHandler;
};
 
//------------------------------------------------------------------
// 초등수학상담 상속 클래스
class 초등수학상담 : public 수학상담문의
{
public:
    초등수학상담(수학상담문의* pHandle) : 수학상담문의(pHandle) {}
 
public:
    void 상담문의(문의자* pUser) override
    {
        if (dynamic_cast<초등학생*>(pUser) != NULL)
            cout << "초등학생 문의 상담입니다." << endl;
        else
            수학상담문의::상담문의(pUser);
    }
};
 
//------------------------------------------------------------------
// 중등수학상담 상속 클래스
class 중등수학상담 : public 수학상담문의
{
public:
    중등수학상담(수학상담문의* pHandle) : 수학상담문의(pHandle) {}
 
public:
    void 상담문의(문의자* pUser) override
    {
        if (dynamic_cast<중학생*>(pUser) != NULL)
            cout << "중학생 문의 상담입니다." << endl;
        else
            수학상담문의::상담문의(pUser);
    }
};
 
//------------------------------------------------------------------
// 고등수학상담 상속 클래스
class 고등수학상담 : public 수학상담문의
{
public:
    고등수학상담(수학상담문의* pHandle) : 수학상담문의(pHandle) {}
 
public:
    void 상담문의(문의자* pUser) override
    {
        if (dynamic_cast<고등학생*>(pUser) != NULL)
            cout << "고등학생 문의 상담입니다." << endl;
        else
            수학상담문의::상담문의(pUser);
    }
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    문의자* pUser = new 고등학생();
    수학상담문의* pHandler = new 초등수학상담(new 중등수학상담(new 고등수학상담(NULL)));
 
    pHandler->상담문의(pUser);
 
    delete pHandler;
    delete pUser;
 
    return 0;
}


예제 결과)








코드 중복 해결을 위한 Template 활용


위 코드나 다이어그램에서 보는것처럼 상담 클래스가 늘어남에 따라 코드 중복과 함께 코드의 양도 늘어날 수 밖에 없게 되는데, 이에 대해 해결을 위해 template를 활용하여 단순화 시키도록 한다



예제) Template 예제


템플릿을 활용해서 간단히 수학상담템플릿으로 단순화시켰다.



Template 예제 코드)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//------------------------------------------------------------------
// 문의자 인터페이스 클래스
class 문의자 
{
public:
    virtual const TCHAR* Class() = 0;
};
class 초등학생 : public 문의자
{
public:
    const TCHAR* Class() { return "초등학생"; }
};
class 중학생 : public 문의자 
{
public:
    const TCHAR* Class() { return "중학생"; }
};
class 고등학생 : public 문의자 
{
public:
    const TCHAR* Class() { return "고등학생"; }
};
 
//------------------------------------------------------------------
// 수학상담문의 인터페이스 클래스
class 수학상담문의
{
public:
    수학상담문의(수학상담문의* pHandle) : pHandler(pHandle) {}
    ~수학상담문의() { if (pHandler) delete pHandler; }
 
public:
    virtual void 상담문의(문의자* pUser)
    {
        if (pHandler != NULL)
            pHandler->상담문의(pUser);
    }
 
private:
    수학상담문의* pHandler;
};
 
//------------------------------------------------------------------
// 수학상담 템플릿 클래스
template<typename T>
class 수학상담템플릿 : public 수학상담문의
{
public:
    수학상담템플릿(수학상담문의* pHandle) : 수학상담문의(pHandle) {}
 
public:
    void 상담문의(문의자* pUser) override
    {
        if (dynamic_cast<T*>(pUser) != NULL)
            cout << pUser->Class() << " 문의 상담입니다." << endl;
        else
            수학상담문의::상담문의(pUser);
    }
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    문의자* pUser = new 고등학생();
    수학상담문의* pHandler = new 수학상담템플릿<초등학생>(new 수학상담템플릿<중학생>(new 수학상담템플릿<고등학생>(NULL)));
 
    pHandler->상담문의(pUser);
 
    delete pHandler;
    delete pUser;
 
    return 0;
}


Template 예제 결과)