프로토타입 (Prototype)
원형이 되는 인스턴스를 사용하여 생성할 객체의 종류를 명시하고, 이렇게 만든 견본을 복사해서 새로운 객체를 생성합니다.
다른말로 원형이라고도 부른다.
예제
핵앤슬래시 게임을 만들다고 해보자. 몬스터들은 스포너를 통해 게임에 스폰되고 몬스터별로 스포너가 존재한다.
class Monster {};
class Ghost : public Monster {};
...
class Spawner {
public:
virtual ~Spawner() {}
virtual Monster* spawnMonster() = 0;
};
class GhostSpawner : public Spawner {
public:
virtual Monster* spawnMonster() { return new Ghost(); }
};
...
만약 이런식으로 구현하게 된다면 몬스터의 종류가 늘어나면 스포너도 그에 맞춰서 늘어나기 때문에 코드의 중복도 발생하므로 비효율적이다.
하지만 프로토타입 패턴을 적용하면 자신과 비슷한 객체를 스폰(복제)할 수 있기 때문에 개별적인 스포너를 추가하지 않아도 된다.
class Monster {
public:
virtual ~Monster() {}
virtual Monster* clone() = 0; // 복제를 위한 순수가상함수 추가
};
class Ghost : public Monster {
public:
Ghost(int health, int speed) : health_(health), speed_(speed) {}
virtual Monster* clone() { return new Ghost(health_, speed_); }
private:
int health_;
int speed_;
};
class Spawner {
public:
Spawner(Monster* prototype) : prototype_(prototype) {}
Monster* spawnMonster() { return prototype->clone(); }
private:
Monster* prototype_;
};
몬스터별로 스포너를 만드는 대신 몬스터의 기본 클래스에 복제를 위한 인터페이스를 추가한다.
몬스터들은 동일한 인터페이스를 제공하기 때문에 스포너는 스폰(복제)할 원본 객체를 보관했다가 필요할 때 인터페이스를 통해 스폰하면 된다.
스포너에 있던 인터페이스가 몬스터로 옮겨갔다고 보면 될것같다.
Monster* ghostPrototype = new Ghost(15, 3); // 원형 객체 생성
Spawner* ghostSpawner = new Spawner(ghostPrototype); // 앞으로 health 15, speed 3의 객체 복제
프로토타입 패턴의 장점은 클래스뿐만 아니라 상태도 같이 복제한다는 점이다. 원형 객체를 생성할 때 설정된 상태 그대로 복제한다.
그래도 결과적으로는 코드 양이 크게 줄어들지는 않는다.
또한 요즘은 몬스터 종류별로 클래스를 만들기보다는 컴포넌트나 타입객체로 모델링 하는것이 더 선호된다.
다른 구현 방법으로는 함수 포인터, 템플릿, 일급 자료형을 사용하는 방법이 있다.
데이터 모델링을 위한 프로토타입
게임에서는 데이터 모델을 정의할 때, 보통 JSON을 많이 사용한다.
{
"이름": "고블린 보병",
"기본체력": 20,
"최대체력": 30,
"내성": ["추위", "독"],
"약점": ["불", "빛"]
}
{
"이름": "고블린 마법사",
"기본체력": 20,
"최대체력": 30,
"내성": ["추위", "독"],
"약점": ["불", "빛"]
"마법": ["화염구", "번개 화살"]
}
{
"이름": "고블린 궁수",
"기본체력": 20,
"최대체력": 30,
"내성": ["추위", "독"],
"약점": ["불", "빛"]
"공격방법": ["단궁"]
}
키/값 구조로 위와 같이 정의되어 있을 것이다. 하지만 중복되는 부분이 매우 많다.
{
"이름": "고블린 보병",
"기본체력": 20,
"최대체력": 30,
"내성": ["추위", "독"],
"약점": ["불", "빛"]
}
{
"이름": "고블린 마법사",
"프로토타입": "고블린 보병",
"마법": ["화염구", "번개 화살"]
}
{
"이름": "고블린 궁수",
"프로토타입": "고블린 보병",
"공격방법": ["단궁"]
}
추상 프로토타입을 만드는 대신 기존의 객체를 위임함으로써 코드 중복을 대폭 줄일 수 있다.
{
"이름": "참수의 마법검",
"프로토타입": "롱소드",
"보너스대미지": "20"
}
기존 아이템에 추가옵션이 붙는 아이템의 경우에도 동일하게 적용할 수 있다.
프로토타입은 결과적으로 기존 객체를 복제하거나, 추상 클래스를 만드는 대신 위임을 통해 코드의 중복을 회피하는 기법이라고 볼 수 있을것 같다.
'도서 > 게임 프로그래밍 패턴' 카테고리의 다른 글
디자인 패턴 다시 보기 : 상태 (0) | 2022.12.14 |
---|---|
디자인 패턴 다시 보기 : 싱글턴 (0) | 2022.12.14 |
디자인 패턴 다시 보기 : 관찰자 (0) | 2022.12.12 |
디자인 패턴 다시 보기 : 경량 (0) | 2022.12.12 |
디자인 패턴 다시 보기 : 명령 (0) | 2022.12.12 |