Thinking Different







Strategy Pattern - 전략 패턴


  • 동적으로 알고리즘을 교체할 수 있는 구조
  • 알고리즘 인터페이스를 정의하고, 각각의 알고리즘 클래스별로 캡슐화하여 각각의 알고리즘을 교체 사용 가능하게 한다
  • 즉, 하나의 결과를 만드는 목적(메소드)은 동일하나, 그 목적을 달성할 수 있는 방법(전략, 알고리즘)이 여러가지가 존재할 경우
  • 기본이 되는 템플릿 메서드(Template Method Pattern) 패턴과 함께 가장 많이 사용되는 패턴 중에 하나이다




샘플 코드

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
//------------------------------------------------------------------
// Strategy Interface class
class Strategy
{
public:
    virtual void AlgorithmInterface() = 0;
};
 
//------------------------------------------------------------------
// Strategy Algorithm A
class ConcreteStrategyA : public Strategy
{
public:
    void AlgorithmInterface() override { cout << "Processed by Strategy A" << endl; }
};
 
//------------------------------------------------------------------
// Strategy Algorithm B
class ConcreteStrategyB : public Strategy
{
public:
    void AlgorithmInterface() override { cout << "Processed by Strategy B" << endl; }
};
 
//------------------------------------------------------------------
// Strategy Algorithm C
class ConcreteStrategyC : public Strategy
{
public:
    void AlgorithmInterface() override { cout << "Processed by Strategy C" << endl; }
};
 
//------------------------------------------------------------------
// Context
class Context
{
public:
    Context() : m_pStrategy(0) {}
    ~Context() { if (m_pStrategy) delete m_pStrategy; }
public:
    void ChangeStrategy(Strategy* pStrategy) 
    { 
        if (m_pStrategy) delete m_pStrategy; 
        m_pStrategy = pStrategy;
    }
    void ContextInterface() { m_pStrategy - > AlgorithmInterface(); }
private:
    Strategy* m_pStrategy;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    Context* pContext = new Context();
    pContext->ChangeStrategy(new ConcreteStrategyA());
    pContext->ContextInterface();
 
    pContext->ChangeStrategy(new ConcreteStrategyB());
    pContext->ContextInterface();
 
    pContext->ChangeStrategy(new ConcreteStrategyC());
    pContext->ContextInterface();
    delete pContext;
 
    return 0;
}




예제를 통한 전략 패턴(Strategy Pattern) 알아보기


예제 1) 정렬 알고리즘


다음 그림과 같이 데이터 정렬을 하는 알고리즘이 있다고 한다면, 무수히 많은 알고리즘이 존재할 것이다. 그럴 경우 하나의 정렬 인터페이스를 정의하고 각각의 알고리즘들을 하위 클래스에서 작성하고 캡슐화하여 쓰고 싶은것을 골라서 사용할 수 있는(전략적) 구조로 설계한다.



예제 코드 1)

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
//------------------------------------------------------------------
// 정렬 인터페이스
class 정렬_인터페이스
{
public:
    virtual void 정렬() = 0;
};
 
//------------------------------------------------------------------
// 퀵 정렬 알고리즘 클래스
class 퀵정렬 : public 정렬_인터페이스
{
public:
    void 정렬() override { cout << "퀵 정렬" << endl; }
};
 
//------------------------------------------------------------------
// 버블 정렬 알고리즘 클래스
class 버블정렬 : public 정렬_인터페이스
{
public:
    void 정렬() override { cout << "버블 정렬" << endl; }
};
 
//------------------------------------------------------------------
// 머지 정렬 알고리즘 클래스
class 머지정렬 : public 정렬_인터페이스
{
public:
    void 정렬() override { cout << "머지 정렬" << endl; }
};
 
//------------------------------------------------------------------
// 정렬 관리자 클래스
class 정렬관리자
{
public:
    정렬관리자() : pInterface(0) {}
    ~정렬관리자() { if (pInterface) delete pInterface; }
 
public:
    void 정렬() { pInterface->정렬(); }
    void 인터페이스_설정(정렬_인터페이스* _interface) 
    { 
        if (pInterface) delete pInterface; 
        pInterface = _interface; 
    }
 
private:
    정렬_인터페이스* pInterface;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    정렬관리자 *pManager = new 정렬관리자();
    pManager->인터페이스_설정(new 버블정렬());
    pManager->정렬();
 
    pManager->인터페이스_설정(new 퀵정렬());
    pManager->정렬();
 
    pManager->인터페이스_설정(new 머지정렬());
    pManager->정렬();
    delete pManager;
 
    return 0;
}


예제 결과 1)





예제 2) 농구 점수 (슛팅) 알고리즘


이번 예제도 위와 동일하며, 전략(슛팅)이 여러가지가 존재하고, 농구선수(context)가 필요할 때마다 알고리즘(슛)을 변경, 교체해서 사용할 수 있음을 보여주는 전형적인 전략 패턴이다.


예제 코드 2)

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
//------------------------------------------------------------------
// 슛팅 인터페이스
class 슛팅_인터페이스
{
public:
    virtual void 슛() = 0;
};
 
//------------------------------------------------------------------
// 슛팅 (2점) 알고리즘 클래스
class 슛팅_2점 : public 슛팅_인터페이스
{
public:
    void 슛() override { cout << "2점 슛" << endl; }
};
 
//------------------------------------------------------------------
// 슛팅 (3점) 알고리즘 클래스
class 슛팅_3점 : public 슛팅_인터페이스
{
public:
    void 슛() override { cout << "3점 슛" << endl; }
};
 
//------------------------------------------------------------------
// 슛팅 (덩크슛) 알고리즘 클래스
class 슛팅_덩크슛 : public 슛팅_인터페이스
{
public:
    void 슛() override { cout << "덩크 슛" << endl; }
};
 
//------------------------------------------------------------------
// 농구선수 클래스
class 농구선수
{
public:
    농구선수() : m_pInterface(0) {}
    ~농구선수() { if (m_pInterface) delete m_pInterface; }
public:
    void 슛_설정(슛팅_인터페이스* pInterface) 
    { 
        if (m_pInterface) delete m_pInterface;
        m_pInterface = pInterface; 
    }
    void 슛() { m_pInterface->슛(); }
private:
    슛팅_인터페이스* m_pInterface;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
    농구선수* pPlayer = new 농구선수();
    pPlayer->슛_설정(new 슛팅_2점());
    pPlayer->슛();
 
    pPlayer->슛_설정(new 슛팅_3점());
    pPlayer->슛();
 
    pPlayer->슛_설정(new 슛팅_덩크슛());
    pPlayer->슛();
    delete pPlayer;
 
    return 0;
}


예제 결과 2)