1. 기존의 메인프레임 작업을 뒤엎고 새로운 방식으로 짜보는데 주력한다.

2. 오류는 발전의 어머니다. 항상 기억하자.

3. 만약 코드를 복사하는 일이 있다면 코드를 한줄한줄 잘 주석을 달자.

4. 하나의 프로젝트안에 다른 다수의 하위프로젝트가 들어갈수 있음을 염두에 두고 디자인하자.

5. 디자인패턴을 쓴다면 왜 그것을 쓰고 왜 그렇게 써야하는지 생각하자. 또한 섞어서 써야할 경우를 항시 생각하자.

Posted by JJOREG

싱글톤패턴 --------------------------------------------------------------------------------------------------

싱글톤 패턴은 객체를 1개만 생성하게 하는 문제를 해결해주는 패턴이다.

자 생각해보자. 어떠한 cpp에서 어떠한 클래스에 대해서 알고 있다면 그 클래스를 지속적으로 객체화 시킬수 있을 것이다.

싱글톤 패턴이란 클래스의 생성자체를 클래스 내부의 함수로 국한시키고 그 함수를 통해서만 객체를 공개함으로서 그 함수의 객체를 1개로 유지시키는 패턴이다.

싱글톤은 일반적으로 사람들이 클래스에 대한 정의만 알면 어디에서든 가져다 쓸 수 있다는 점에만 집중하는데 

싱글톤 패턴의 가장 중요한 점은 생성에 관련해서는 어떤방식으로 함수를 호출해도 객체는 유저가 정한 1개만큼 호출된다는 것이다.


즉 가장 중요한점은.

1. 객체의 생성은 그 클래스의 함수를 통해서만 이루어진다.


싱글톤은 다양한 방법으로 구현이 가능하지만 처음 프로그래밍을 배울때 단순히 편하다는 이유로 마구 남발하는 경우가 있었다.

어떤 유명프로그래머는 싱글톤패턴은 궁극적으로는 결국 프로그램의 구조를 나쁘게한다고 이야기하기도 했다.

현재 내가 생각하는 단점은 한가지이다. 

싱글톤패턴은 프로그래머의 고민을 없애버린다.

고민이란 점점더 발전하여 더 좋은 구조를 짜나가는 것을 의미한다고 본다.

그런데 싱글톤은 그 길을 그냥 직빵으로 뚫어버릴 때가 많다.

게임 프로그래밍이란 결국 값에 대한 저장이고 값에 대한 조작이다.

싱글톤은 그 값에 대한 접근을 너무 용이하게 하는 경우가 많다.


코드 -----------------------------------------------------------------------------------------------

패턴적용 스토리는 딱히 없다. 싱글톤이란 말그대로 응용하기에 따라서 무궁무진하게 적용이 가능하다. 말그대로 모든 패턴과 연관지을수 있기 때문이다.


도도하게 떠있는 싱글톤 이름에 어울리는 uml이다.


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
#pragma once
#include "include.h"
 
class CSingleTon
{
private:
    int        iNum;
 
public:
    void SetiNum(int i)
    {
        cout << i << "숫자 바꿈" << endl;
        iNum = i;
    }
 
private:
    static CSingleTon* m_pInst;
 
public:
    static CSingleTon* GetInst( void )
    {
        if(m_pInst == NULL)
            m_pInst = new CSingleTon;
        return m_pInst;
    }
 
    static void DestroyInst( void )
    {
        if(m_pInst) { SAFE_DELETE(m_pInst); }
    }
 
private:
    CSingleTon( void ) {}
 
public:
    ~CSingleTon( void ) {}
};
 
CSingleTon* CSingleTon::m_pInst = NULL;
 
#include "stdafx.h"
#include "include.h"
#include "SingleTon.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
 
    for(int i = 0 ; i < 10 ; ++i)
    {
        CSingleTon::GetInst()->SetiNum(i);
    }
 
    CSingleTon::DestroyInst();
    return 0;
}
 
 



보면 알겠지만 CSingleTon클래스의 생성자는 private로 설정되어있다. 즉 외부에서는 이 클래스에 대한 객체를 생성이 불가능하다. 

하지만 static로 선언된 자기자신에 대한 포인터를 가지고 있고 GetInst()라는 함수를 통해서 이 포인터를 동적할당하여 힙영역에 할당한다. 

어디에서 먼저 시작했건 이 클래스에 대한 인스턴스는 단 1개만 유지된다.

static로 선언되어있기 때문에 그것은 더욱더 보장된다.

즉 클래스에 대한 접근권한을 GetInst()로 국한시키고, 그안에서 생성할수 있는 개수를 제한한다.

그렇게 생성된 객체를 통해서 내부의 맴버함수에 접근할수 있게 됨으로서 언제 어디서든지 헤더만 추가하면 이 클래스에 대한 접근과 고유한 객체생성에 대한 보장을 받게 된다.

Posted by JJOREG

프로토타입 --------------------------------------------------------------------------------------------------

프로토타입 패턴은 하나의 원형객체를 두고 그 객체를 복사해서 새로운 객체를 생성하는 패턴을 말한다.

요약하자면 

CPlayer* ProtoMonster = new Obj;

CPlayer* CreateMonster = ProtoMonster;

이런식으로 새롭게 생길 객체에 또하나의 객체를 대입하게 된다.

하지만 이렇게 할경우 주소값만 복사된 꼴이 되기 때문에 CreateMonster가 새로운 몬스터라고 할수는 없을 것이다.

새로운 방법이 필요하니 그에 대한 방법과 코드에 대한 방법을 보자.

아래의 코드와 패턴을 보면서 알아보도록 하자.


패턴 적용 스토리 -----------------------------------------------------------------------------------------------

당신은 몬스터를 생성하는 구조를 짜게 되었다. 하지만 몬스터가 등장하는 패턴을 보니 같은 몬스터가 여러개 등장하게 되어있다.

이제 플레이어를 만들어 보도록 하자.






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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma once
 
#include "stdafx.h"
#include "include.h"
 
enum MONSTER_TYPE
{
    MONSTER_TYPE_0,
    MONSTER_TYPE_1,
    MONSTER_TYPE_2,
    MONSTER_TYPE_LAST
};
 
typedef struct tagMonster
{
    int    At;
    int    Dp;
    int Hp;
    int TextureIndex;
 
 
    void Setting(int iLevel)
    {
        At = (iLevel+1) * 10;
        Dp = (iLevel+1) * 10;
        Hp = (iLevel+1) * 10;
        TextureIndex = iLevel;
    }
 
    tagMonster(void)
    {
        At = 0;
        Dp = 0;
        Hp = 0;
        TextureIndex = 0;
    };
 
}MONSTERINFO, *PMONSTERINFO;
 
class CMonster
{
private:
    MONSTERINFO            tMonsterInfo;
 
public:
    void SetMonsterInfo(MONSTERINFO& rInfo)
    {
        tMonsterInfo = rInfo;
    }
 
public:
    CMonster* Clone(void)
    {
        switch(tMonsterInfo.TextureIndex)
        {
        case MONSTER_TYPE_0:
            cout << "CREATE MONSTER_TYPE_0" << endl;
            break;
        case MONSTER_TYPE_1:
            cout << "CREATE MONSTER_TYPE_1" << endl;
            break;
        case MONSTER_TYPE_2:
            cout << "CREATE MONSTER_TYPE_2" << endl;
            break;
        }
 
        return new CMonster(*this);
    }
 
public:
    CMonster(void) {}
    ~CMonster(void) {}
};
 
class CMonsterCreator
{
private:
    map<MONSTER_TYPE, CMonster*>        m_MonsterProtoMap;
 
public:
    void CreateProtoMonster(void)
    {
        for(int i = 0 ; i < MONSTER_TYPE_LAST ; ++i)
        { 
            CMonster* pMonster= new CMonster;
            MONSTERINFO MonsterInfo;
            MonsterInfo.Setting(i);
            pMonster->SetMonsterInfo(MonsterInfo);
            m_MonsterProtoMap.insert(map<MONSTER_TYPE, CMonster*>::value_type((MONSTER_TYPE)i, pMonster));
        }
    }
 
public:
    CMonster* CreateFieldMonster(MONSTER_TYPE eMonsterType)
    {
        map<MONSTER_TYPE, CMonster*>::iterator FindIter = m_MonsterProtoMap.find(eMonsterType);
 
        if(FindIter == m_MonsterProtoMap.end())
            return NULL;
 
        CMonster* pMonster = FindIter->second->Clone();
 
        return pMonster;
    }
 
public:
    CMonsterCreator(void) {}
    ~CMonsterCreator(void) {}
};
 
#include "stdafx.h"
#include "include.h"
#include "Prototype.h"
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    CMonsterCreator MonsterCreate;
 
    MonsterCreate.CreateProtoMonster();
 
    CMonster* pMonster;
 
    for(int i = 0; i < 10 ; ++i)
    {
        srand((unsigned)time(NULL));
        int j = rand() % 3;
        Sleep(1000);
        pMonster = MonsterCreate.CreateFieldMonster( ( MONSTER_TYPE )( j ) );
    }
 
    return 0;
}
 
 
 



보면 알겠지만 CreateProtoMonster()함수를 통해서 초반에 몬스터를 생성 해놓고 

그 이후에 생성되는 것은 CreateFieldMonster()함수를 통해서 내부에 저장되어있는 몬스터의 값을 복제해서 새로운 객체를 생성한다. 

하지만 이미 포인터로 만들어져 있기 때문에 단순 대입으로는 주소값대 주소값의 복사가 되어버리므로 리턴 형식을 자기자신의 새로운 형으로해서 자기 자신의 값을 채워넣어 복사하는

Clone함수를 사용해서 값을 새롭게 생성하여 복사해주면 된다.

'게임개발공부 > 디자인패턴' 카테고리의 다른 글

메인프레임 작업 01  (0) 2013.12.21
생성패턴 <싱글톤 패턴>  (0) 2013.12.18
쓰레드 환경에서 싱글톤?  (0) 2013.12.16
생성패턴 <팩토리 메서드>  (0) 2013.12.15
생성패턴 <빌더>  (0) 2013.11.24
Posted by JJOREG

2개의 쓰레드에서 동시에 하나의 싱글톤을 참조하게 되면?


싱글톤은 단 1개의 객체를 생성하는것이 장점인 패턴이다.


그런데 1개로 생성한 객체 내부에 있는 데이터를 2개의 쓰레드에서 동시에 접근한다면 어떤 일이 벌어질까?


... 애초에 그렇게 쓰면 안돼겠지만 그 데이터(전역이나 마찬가지 이므로)에 동기화를 걸어놓는게 좋을거 같다.


쌤한테 물어보자.

'게임개발공부 > 디자인패턴' 카테고리의 다른 글

생성패턴 <싱글톤 패턴>  (0) 2013.12.18
생성패턴 <프로토 타입>  (0) 2013.12.17
생성패턴 <팩토리 메서드>  (0) 2013.12.15
생성패턴 <빌더>  (0) 2013.11.24
생성패턴 <추상팩토리>  (0) 2013.11.20
Posted by JJOREG

팩토리메서드 --------------------------------------------------------------------------------------------------

팩토리 메서드는 객체를 생성하기 위한 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할 지에 대한 결정은 서브클래스가 내리도록 하는 패턴입니다.

상속과 다형성을 이용하는 방법입니다.


패턴 적용 스토리 -----------------------------------------------------------------------------------------------

게임내에서 아이템드랍방식을 생각해보면 몬스터가 죽였을때 아이템이 생성되어야 합니다.

아이템의 종류는 무기, 방패, 갑옷의 세종류가 있으며 타입마다 다른 아이템을 사용하여 리턴합니다.

팩토리 메서드패턴은 생성 클래스 자체를 추상화하여 각 아이템별 생성클래스를 생성하거나 혹은 생성 클래스 내부에서 인자값을 받아 그에 맞는 아이템클래스를 생성할수 있습니다.

이번에는 인자값을 받아서 그에 맞는 아이템 클래스를 생성하는 패턴으로 가보겠습니다.





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
#pragma once
 
#include "stdafx.h"
 
enum ITEM_TYPE
{
    ITEM_TYPE_WEAPON,
    ITEM_TYPE_SHIELD,
    ITEM_TYPE_ARMOR,
    ITEM_TYPE_COUNT
};
 
class CItem
{
public:
    CItem(void) {}
    ~CItem(void) {}
};
 
class CWeapon
    : public CItem
{
public:
    CWeapon(void) {}
    ~CWeapon(void) {}
};
 
class CShield
    : public CItem
{
public:
    CShield(void) {}
    ~CShield(void) {}
};
 
class CArmor
    : public CItem
{
public:
    CArmor(void) {}
    ~CArmor(void) {}
};
 
class CItemCreator
{
public:
    CItem* CreateItem(ITEM_TYPE eItemType)
    {
        CItem* pItem = NULL;
        switch(eItemType)
        {
        case ITEM_TYPE_WEAPON:
            pItem = new CWeapon;
            cout << "무기가 드랍됨" << endl;
            break;
        case ITEM_TYPE_SHIELD:
            pItem = new CShield;
            cout << "방패가 드랍됨" << endl;
            break;
        case ITEM_TYPE_ARMOR:
            pItem = new CArmor;
            cout << "갑옷이 드랍됨" << endl;
            break;
        default:
            cout << "꽝 아이템 없음 ㅋㅋㅋ" << endl;
            break;
        }
 
        return pItem;
    }
 
public:
    CItemCreator(void) {}
    ~CItemCreator(void) {}
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    srand((unsigned)time(NULL));
    CItemCreator ItemCreate;
 
    for(int i = 0; i < 10 ; ++i)
    {
        srand((unsigned)time(NULL));
        int j = rand() % 5;
        Sleep(500);
        ItemCreate.CreateItem( (ITEM_TYPE)(j) );
    }
 
    return 0;
}
 



Posted by JJOREG

빌더패턴 --------------------------------------------------------------------------------------------------

  당신은 게임중 스테이지를 생성하는 파트를 만들고 있습니다.

  스테이지는 수중맵, 공중맵, 그리고 지상맵으로 나뉩니다. 

  파일을 입력받아서 스테이지를 만드는것에 있어서 단계는 세가지로 나눕니다

  1. 지형및 배경생성.

  2. 몬스터생성.

  3. 오브젝트 생성.

  그런데 스테이지를 만듬에 있어서 세가지 스테이지는 전혀 다른 방식의 함수와 과정을 거쳐서 생성되어야 합니다.

  각 지형마다 특색을 주기 위해서 몬스터나 전반적인 생성방식이 다를 경우 생성하기위한 클래스나 코드가 비대해질수가 있습니다. 그것을 방지하기 위한 패턴이라고 생각해 주시면 됩니다.



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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <list>
 
using namespace std;
 
template <typename T>
void SAFE_DELETE(T& pointer)
{
    if(pointer)
    {
        delete pointer;
        pointer = NULL;
    }
}
 
enum STAGE_TYPE
{
    STAGE_TYPE_SKY,
    STAGE_TYPE_SEA,
    STAGE_TYPE_LAND
};
 
enum STAGE_COUNT
{
    STAGE_COUNT_MAXMONSTER = 10,
    STAGE_COUNT_MAXMONSTERTYPE = 20,
    STAGE_COUNT_MAXTERRAIN = 10,
    STAGE_COUNT_MAXTERRAINTYPE = 10
};
 
typedef struct tagStageFile
{
    STAGE_TYPE  eStageType;
    int            iBackGround;
    int            listMonster[STAGE_COUNT_MAXMONSTER];
    int            listTerrain[STAGE_COUNT_MAXTERRAIN];
 
public:
    tagStageFile(void)    
    { 
        eStageType = (STAGE_TYPE)(rand() % 3); 
        iBackGround = rand() % 10;
 
        memset(listMonster, -1, sizeof(int)*STAGE_COUNT_MAXMONSTER);
        memset(listTerrain, -1, sizeof(int)*STAGE_COUNT_MAXMONSTER);
 
        for (int i = 0; i < (rand() % 10) ; ++i)
        {
            listMonster[i] = rand() % STAGE_COUNT_MAXMONSTERTYPE;
            listTerrain[i] = rand() % STAGE_COUNT_MAXTERRAINTYPE;
        }
    }
 
}STAGEFILE;
 
class CStage
{
private:
    STAGE_TYPE  eStageType;
    int            iBackGround;
    list<int>    listMonster;
    list<int>    listTerrain;
 
public:
    void SetStageType(STAGE_TYPE& _eStageType) { eStageType = _eStageType; }
    void SetBackGround(int& _iBackGround) { iBackGround = _iBackGround; }
    void SetMonster(int& _MonsterType) { listMonster.push_back(_MonsterType); }
    void SetTerrain(int& _TerrainType) { listTerrain.push_back(_TerrainType); }
 
public:
    CStage(void) {}
    ~CStage(void) {}
};
 
class CStageBuilder
{
protected:
    CStage    m_Stage;
 
public:
    CStage GetStage(void) { return m_Stage; }
    
    virtual void CreBackGround(int _BackGroundIndex) = 0;
    virtual void CreMonster(int _MonsterType) = 0;
    virtual void CreTerrain(int _TerrrainType) = 0;
 
public:
    CStageBuilder(void) {}
    virtual ~CStageBuilder(void) {}
};
 
class CSkyBuilder
    : public CStageBuilder
{
    virtual void CreBackGround(int _BackGroundIndex) 
    { cout << _BackGroundIndex << " 번 하늘이미지 배경으로 선택" << endl; m_Stage.SetBackGround(_BackGroundIndex); }
    virtual void CreMonster(int _MonsterType) 
    { if(_MonsterType != -1) cout << _MonsterType << " 타입 공중몬스터 생성" << endl; m_Stage.SetMonster(_MonsterType); }
    virtual void CreTerrain(int _TerrrainType) 
    { if(_TerrrainType != -1) cout << _TerrrainType << " 타입 공중지형지물생성" << endl; m_Stage.SetMonster(_TerrrainType); }
 
public:
    CSkyBuilder(void) {}
    virtual ~CSkyBuilder(void) {}
};
 
class CSeaBuilder
    : public CStageBuilder
{
    virtual void CreBackGround(int _BackGroundIndex) 
    { cout << _BackGroundIndex << " 번 바다이미지 배경으로 선택" << endl; m_Stage.SetBackGround(_BackGroundIndex); }
    virtual void CreMonster(int _MonsterType) 
    { if(_MonsterType != -1) cout << _MonsterType << " 타입 바다몬스터 생성" << endl; m_Stage.SetMonster(_MonsterType); }
    virtual void CreTerrain(int _TerrrainType) 
    { if(_TerrrainType != -1) cout << _TerrrainType << " 타입 바다지형지물 생성" << endl; m_Stage.SetMonster(_TerrrainType); }
 
public:
    CSeaBuilder(void) {}
    virtual ~CSeaBuilder(void) {}
};
 
class CLandBuilder
    : public CStageBuilder
{
    virtual void CreBackGround(int _BackGroundIndex) 
    { cout << _BackGroundIndex << " 번 하늘이미지 배경으로 선택" << endl; m_Stage.SetBackGround(_BackGroundIndex); }
    virtual void CreMonster(int _MonsterType) 
    { if(_MonsterType != -1) cout << _MonsterType << " 타입 땅몬스터 생성" << endl; m_Stage.SetMonster(_MonsterType); }
    virtual void CreTerrain(int _TerrrainType) 
    { if(_TerrrainType != -1) cout << _TerrrainType << " 타입 땅지형지물생성" << endl; m_Stage.SetMonster(_TerrrainType); }
 
public:
    CLandBuilder(void) {}
    virtual ~CLandBuilder(void) {}
};
 
class CStageDirector
{
private:
    STAGEFILE       m_tStageFile;
    CStageBuilder* m_cStageBuilder;
 
public:
    void CreStage (STAGEFILE _tStageFile)
    {
        cout << "---스테이지 타입 확인---" << endl;
        switch(_tStageFile.eStageType)
        {
        case STAGE_TYPE_SKY:
            cout << "하늘 스테이지생성시작" << endl;
            m_cStageBuilder = new CSkyBuilder;
            break;
        case STAGE_TYPE_SEA:
            cout << "해저 스테이지생성시작" << endl;
            m_cStageBuilder = new CSeaBuilder;
            break;
        case STAGE_TYPE_LAND:
            cout << "지상 스테이지생성시작" << endl;
            m_cStageBuilder = new CLandBuilder;
            break;
        }
 
        cout << "---스테이지 배경 생성---" << endl;
        m_cStageBuilder->CreBackGround(m_tStageFile.iBackGround);
 
        cout << "---스테이지 몬스터 생성---" << endl;
        for(int i = 0 ; i < STAGE_COUNT_MAXMONSTER ; ++i)
        { m_cStageBuilder->CreMonster(m_tStageFile.listMonster[i]); }
 
        cout << "---스테이지 지형지물 생성---" << endl;
        for(int i = 0 ; i < STAGE_COUNT_MAXTERRAIN ; ++i)
        { m_cStageBuilder->CreTerrain(m_tStageFile.listTerrain[i]); }
    }
 
public:
    CStage GetResultStage(void) { m_cStageBuilder->GetStage(); }
 
public:
    CStageDirector(void) {}
    ~CStageDirector(void) {    SAFE_DELETE(m_cStageBuilder); }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
 
    srand((unsigned)time(NULL));
    STAGEFILE StageFile;
    CStageDirector StageDirector;
 
    StageDirector.CreStage(StageFile);
 
    return 0;
}
 

실행결과

Posted by JJOREG


Abstract Factory.zip

생성패턴이란? -----------------------------------------------------------------------------------------------

"생성패턴을 이용하여 객체의 인스턴스화를 무엇을? 누가? 어떻게? 언제? 생성할 것인지를 결정하는 것이다."

하지만 내가 사용하는 목적은 간단하다. 클래스에 책임 몰아주기가 가능하기 때문이다.

"이 클래스에서 종합적으로 모든 객체를 생성할꺼야." 라고 기억만 해두면 다음에는 객체가 생성되는 곳에 문제가 생겼을 때 되도록 한 클래스나 하나의 구현파일만 살펴봐도 문제가 해결될 수 있기 때문이다.


추상팩토리 --------------------------------------------------------------------------------------------------

 제 식대로 추상팩토리를 구현해 봤습니다. 

 플레이어라는 클래스는 직업, 무기, 스킬을 가지고 있고 그것을 지급받아야 합니다.

 지급받아야 되는 객체들은 모두다 추상클래스를 부모로둔 하위 클래스들입니다.

 추상 팩토리에서는 각 조합에 맞춰서 플레이어에게 지급할수 있게 합니다.

 단순히 new를 하는것이 아니라 그것을 팩토리에 맡기고 다형성을 이용하여 팩토리의 속성에 따라서 적합한 플레이어를 만들어 낼 수 있게 됩니다.


 



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
108
109
110
111
112
113
114
115
116
117
#include "stdafx.h"
#include <iostream>
 
using namespace std;
 
template <typename T>
void SAFE_DELETE(T& pointer)
{
    if(pointer)
    {
        delete pointer;
        pointer = NULL;
    }
}
 
class CJob
{
public:
    virtual ~CJob(void) {}
};
 
class CFighter : public CJob { virtual ~CFighter(void) {} };
class CMage       : public CJob { virtual ~CMage(void) {}    };
 
class CWeapon
{
public:
    virtual ~CWeapon(void) {}
};
 
class CSword : public CWeapon { virtual ~CSword(void) {} };
class CStaff : public CWeapon { virtual ~CStaff(void) {} };
 
class CSkill
{
public:
    virtual ~CSkill(void) {}
};
 
class CStrike : public CSkill        { virtual ~CStrike(void) {} };
class CMagicMissile : public CSkill { virtual ~CMagicMissile(void) {} };
 
class CAbstractFactory
{
public:
    virtual CJob*     CreJob(void) = 0;
    virtual CWeapon* CreWeapon(void) = 0;
    virtual CSkill*  CreSkill(void) = 0;
 
public:
    virtual ~CAbstractFactory(void) {}
};
 
class CMageFactory
    : public CAbstractFactory
{
    virtual CJob*     CreJob(void)        {  cout << "마법사 직업지급" << endl;     return new CMage;            }
    virtual CWeapon* CreWeapon(void)    {  cout << "스태프 지급" << endl;     return new CStaff;            }
    virtual CSkill*  CreSkill(void)        {  cout << "매직미사일을 배웠다!" << endl; return new CMagicMissile;    }    
public:
    CMageFactory(void) { cout << "유저가 마법사를 선택했다." << endl; }
    virtual ~CMageFactory(void) {}
};
 
class CFighterFactory
    : public CAbstractFactory
{
    virtual CJob*     CreJob(void)        { cout << "전사 직업지급" << endl;    return new CFighter; }
    virtual CWeapon* CreWeapon(void)    { cout << "검 지급" << endl;    return new CSword;     }
    virtual CSkill*  CreSkill(void)        { cout << "강타를 배웠다!" << endl; return new CStrike;     }    
 
public:
    CFighterFactory(void) { cout << "유저가 전사를 선택했다." << endl; }
    virtual ~CFighterFactory(void) {}
};
 
class CPlayer
{
private:
    CJob*        m_Job;
    CWeapon*    m_Weapon;
    CSkill*        m_Skill;
 
public:
    CPlayer(void) {}
 
    CPlayer(CJob* pJob, CWeapon* pWeapon, CSkill* pSkill) 
        : m_Job(pJob), m_Weapon(pWeapon), m_Skill(pSkill)
    { }
 
    ~CPlayer(void
    { SAFE_DELETE(m_Job); SAFE_DELETE(m_Weapon); SAFE_DELETE(m_Skill); }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    cout << "*******************구분선*******************" << endl;
 
    CAbstractFactory* pFighterFactory = new CFighterFactory;
 
    CPlayer* pFighter = new CPlayer(pFighterFactory->CreJob(), pFighterFactory->CreWeapon(), pFighterFactory->CreSkill());
    SAFE_DELETE(pFighterFactory);
    SAFE_DELETE(pFighter);
 
    cout << "*******************구분선*******************" << endl;
 
    CAbstractFactory* pMageFactory = new CMageFactory;
 
    CPlayer* pMage = new CPlayer(pMageFactory->CreJob(), pMageFactory->CreWeapon(), pMageFactory->CreSkill());
    SAFE_DELETE(pMageFactory);
    SAFE_DELETE(pMage);
 
    cout << "*******************구분선*******************" << endl;
 
    return 0;
}
 

실행결과


Posted by JJOREG

디자인패턴 시작----------------------------------------------------------------------------------------------

디자인 패턴에 대해서는 한번 훑어 봤지만 각 디자인 패턴에 대해서 실제로 쳐보지 않았으면 제대로 해봤다고 할수가 없을것 같아서 하루에 하나씩이라도 디자인패턴을 콘솔환경에서 시험적으로 쳐보고 추후 패턴을 적용한 코드를 만들때마다 올려보기로 했습니다.

(일단은 이론으로만 파악하려니 손이 근질근질해서 콘솔로 못할것도 아니고...)

일단 디자인패턴 도표부터 봅시다!


디자인패턴 관계도---------------------------------------------------------------------------------------------

약간 상세한 이미지--------------------------------------------------------------------------------------------


표로 정리----------------------------------------------------------------------------------------------


 목적

디자인 패턴 

패턴을 통해 다양화 할 수 있는 부분 

 생성패턴

 추상 팩토리 

 제품 객체군 

 빌더 

 복합 객체 생성방법

 팩토리 메서드

 인스턴스화될 객체의 서브 클래스

 원형

 인스턴스화될 객체 클래스 

 단일체

 클래스의 인스턴스가 하나일 때

 구조패턴

 적응자

 객체에 대한 인터페이스

 가교

 객체 구현

 복합체

 객체의 합성과 구조

 장식자

 서브클래싱 없이 객체의 책임성 

 퍼사드

 서브시스템에 대한 인터페이스

 플라이급

 객체의 저장 비용 

 프록시

 객체 접근 방법

 행동패턴

 책임 연쇄

 요청을 처리하는 객체 

 명령

 요청의 처리 시점과 처리 방법

 해석자

 언어의 문법과 해석 방법

 반복자

 집합 객체 요소들의 접근 방법 및 순회 방법

 중재자

 어떤 객체들이 어떻게 상호 작용하는지

 메멘토

 언제 어떤 정보를 객체의 외부에 저장하는지

 감시자

 다른 객체에 종속적인 객체 수 종속적인 객체들의 상태 변경 방법

 상태

 객체의 상태 

 전략

 알고리즘

 템플릿 매서드

 알고리즘의 단계 

 방문자

 클래스의 변경없이 객체에 적용할 수 있는 연산 

  목표는 제목처럼 하루에 하나씩 쳐보고 이해하고 입니다. 아직 프로젝트가 명확하게 정해지지 않았기 때문에 실제 게임구조에 적용이 힘들겠지만 콘솔 환경에서 혀끝으로 맛이라도 봐보려고 합니다. 그럼 내일 생성패턴의 추상팩토리부터 시작해 보겠습니다.


심플한 UML보기----------------------------------------------------------------------------------------------

1. 클래스 다이어그램적인 측면으로 보기

디자인 패턴은 보게되면 UML방식으로 표현하는 경우가 많으니 기본적인 UML문서 보는법을 기억해 둬야 한다.


1. 추상클래스 (인스턴트 되지 않는 클래스 이미지에서는 AbstractClass로 이텔릭체로 표시한다.) -> 객체화 못한다.

1-1. 추상연산 (추상클래스안에 정의만 하고 연산은 없는 인스턴스 이텔릭체로 표시한다.)

2. 구체클래스 (실제 구현이 이루어지거나 추상클래스가 아닌 클래스 이미지 에서는 ConcreteSubClass) 

3. 상속화살표 표시(Inner화살표 끝이 비어있는 화살표). "ConcreteSubClass는 AbstractClass를 상속하고 있다."

4. 생성화살표 표시(Creates 점선 화살표)  "ConcreteClass는 AbstractClass(ConcreteSubClass)를 인스턴스화 한다."

5. 포함화살표 (Aggregation 화살표 끝에 검은 점찍힌 화살표, 검은 점이 찍힌것은 개수 제한없이 0개 이상을 가지고 있을 수 있다는 뜻) 

"ConcreteClass는 AbstractClass를 맴버로 가지고 있다."

6. 객체 참조 (Object Referencef 끝이 꺽인 선으로된 화살표) ConcreteClass는 AbstractClass의 객체를 참조하고 있다.


2. 클래스 객체 관계적인 측면으로 보기

객체들은 타원으로 표시. 객체간의 관계는 화살표로 표시

다원 가운데에 줄이 있는 경우와 없는 경우의 차이는 줄이 있는 경우에는 구체적으로 객체 내부에 다른 객체를 참조하는 데이터 멤버가 같이 표시된다는 점이다.


3. 클래스 동작적인 측면으로 보기

객체간의 동작 형태를 표현할때는 객체는 사각형으로 표시된다. 시스템 프로그래밍에서 각 쓰레드나 프로세스의 동작을 실행흐름으로 보는 것과 비슷한 형태를 띈다.

각 객체의 세로축은 시간의 흐름을 표시한다. 이때 세로 축의 조그만 사각형은 해당 객체가 동작중인 기간을 의미한다. 한편 어떤 객체가 다른 객체 맴버의 함수를 호출하는것은 그 함수의 명칭과 가로 화살표로 표시한다.

'게임개발공부 > 디자인패턴' 카테고리의 다른 글

생성패턴 <프로토 타입>  (0) 2013.12.17
쓰레드 환경에서 싱글톤?  (0) 2013.12.16
생성패턴 <팩토리 메서드>  (0) 2013.12.15
생성패턴 <빌더>  (0) 2013.11.24
생성패턴 <추상팩토리>  (0) 2013.11.20
Posted by JJOREG
이전버튼 1 2 이전버튼