[디자인 패턴] #4_프로토타입 패턴, Prototype Pattern
게임 디자인 패턴 중 "프로토타입 패턴(Prototype Pattern)"에 대해 알아보겠습니다.
"게임 프로그래밍 패턴"의 5 항목, "프로토타입"에 해당하는 내용입니다.
프로토타입 패턴
원형(Prototype)이 되는 인스턴스를 사용해서 객체의 종류를 명시하고,
이렇게 만든 견본을 복사해서 새로운 객체를 생성합니다.
- GoF의 디자인 패턴 -
"프로토타입 패턴"은 원형이되는 인스턴스를 하나 만들어 새롭게 생성할 객체들의 프로토타입으로 사용하는 디자인 패턴을 말합니다.
역시 이해를 위해 예제를 살펴보겠습니다.
Monster + Sapwner, 몬스터와 스포터
class Monster {
//...
};
1. Ghost 몬스터
class Ghost : public Monster
{
//...
};
2. Demon 몬스터
class Demon : public Monster
{
//...
};
3. Sorcerer 몬스터
class Sorcerer : public Monster
{
//...
};
// 각 몬스터 종류마다 존재하는 Spawner 클래스
class Spawner {
public:
virtual ~Spawner();
virtual Monster* spawnMonster() = 0; // 순수 가상 함수!
};
class GhostSpawner : public Spawner
{
virtual Monster* spawnMonster() override;
};
Monster* GhostSpawner::spawnMonster()
{
return new Ghost();
}
"Monster" (상속하는)클래스들과 이들을 Level에 생성하는 "Spawner" 클래스를 예제로 살펴보겠습니다.
가장 먼저, 눈에 들어오는 것은 "Monster" 클래스와 "Spawner" 클래스의 상속 구조가 동일하다는 것입니다!
표면적으로도 좋은 코드 작성 방법이 아닌 것 같죠...
이러한 하드 코딩을 해결하는 방법으로 우리는 "프로토 타입 패턴"을 활용할 수 있습니다!
2가지 방법을 살펴보겠습니다.
1. Monster 클래스 내부에 clone 인터페이스 상속
#include <iostream>
using namespace std;
class Monster
{
public:
virtual ~Monster();
public:
// 1. Monster 객체를 복제하는 순수 가상 함수
virtual Monster* clone() = 0;
};
class Ghost : public Monster
{
public:
Ghost(int _health, int _speed) :health(_health), speed(_speed) {}
// 2. 상속 받은 순수 가상 함수 "clone()"을 정의합니다, 여기서 깊은 복제를 통해 Ghost를 반환합니다.
virtual Monster* clone()
{
return new Ghost(health, speed);
}
private:
int health;
int speed;
};
class Spawner {
public:
Spawner(Monster* _prototype) : prototype(_prototype){}
virtual ~Spawner();
// 3. 인자로 받은 Prototype(원형 인스턴스)의 "clone()" 가상 멤버 함수를 호출합니다.
virtual Monster* spawnMonster()
{
return prototype->clone();
}
private:
// 4. Spawner 객체 생성 시점에 인자로 받은 Prototype 데이터 멤버
Monster* prototype;
};
int main()
{
Monster* ghostPrototype = new Ghost(15, 3);
Spawner* ghostSpawner = new Spawner(ghostPrototype);
}
위 예제 코드를 통해 "프로토타입 패턴"의 주요 포인트들을 짚어보겠습니다.
먼저, "Monster" 추상 클래스는 "clone()" 순수 가상 함수, 혹은 인터페이스를 하위 클래스들에게 상속합니다.
이를 통해, 각 "Monster" 클래스의 하위 클래스들은 자신의 자료형과 상태가 같은 객체를 반환합니다.
결과적으로, 각 "Monster" 종류들마다 "Spawner" 클래스를 개별적으로 작성할 필요가 없어집니다!
다음으로, "Spawner" 클래스는 생성 시점에 실제 Spawn할 "Monster"의 "Prototype"을 인자로 받습니다.
따라서, "Spawner" 클래스는 "spawnMonster()" 멤버 함수의 내부에서 "Prototype"의 "clone()" 메서드 호출을 반환함으로써, 간단하게 "Prototype" 객체의 복제를 수행할 수 있습니다.
* 프로토타입 패턴의 장점은 단순히 프로토타입의 클래스뿐만아니라 "상태" 또한 복제한다는 점입니다!
2. 템플릿 활용
class Monster
{
public:
virtual ~Monster();
//...
};
class Ghost : public Monster
{
//...
};
class Spawner {
public:
virtual ~Spawner();
virtual Monster* spawnMonster() = 0;
};
template<class T>
class SpawnerFor : public Spawner
{
virtual Monster* spawnMonster() { return new T(); }
};
int main()
{
Spawner* ghostSpawner = new SpawnerFor<Ghost>();
//...
// 간단하게 spawnMonster()를 호출합니다.
Monster* ghost_1 = ghostSpawner->spawnMonster();
}
템플릿을 활용한 "프로토타입 패턴"을 위 예제 코드를 통해 살펴보겠습니다.
"Spawner"를 상속하는 템플릿 클래스 "SpawnerFor"는 데이터 타입에 상관없이,
템플릿 파라미터에 따라서 "spawnMonster" 메서드의 반환 값을 달리합니다.
따라서, 객체별 "Spawner" 클래스들을 개별적으로 정의하는 하드코딩을 더욱 효율적으로 대체합니다!
'언어 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴]#6_상태 패턴, State Pattern (1) | 2022.08.20 |
---|---|
[디자인 패턴]#5_싱글턴 패턴, Singleton Pattern (0) | 2022.08.02 |
[디자인 패턴]#3_관찰자 패턴, Observer Pattern (0) | 2022.07.15 |
[디자인 패턴]#2_경량 패턴, Flyweight Pattern (0) | 2022.07.10 |
[디자인 패턴]#1_명령 패턴, Command Pattern (0) | 2022.07.07 |