[Design Pattern] 행동 패턴 5. 프로토타입 (Prototype)
Table of Contents
프로토타입 (Prototype) #
- 기존 인스턴스를 복제하여 새로운 인스턴스를 만드는 방법이다.
게임에서의 예시 #
- 여러가지 종류의 몬스터들을 스폰하는 스포너를 만들고 싶다.
class Monster {};
class Ghost : public Monster {};
class Demon : public Monster {};
class Sorcerer : 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
{
private:
int health, speed;
public:
Ghost(int h, int s) : health(h), speed(s) {}
// 자기 자신과 똑같은 상태의 객체를 복제한다.
virtual Monster* clone()
{
return new Ghost(health, speed);
}
};
// 이제 하나의 스포너만 있어도 되겠다.
class Spawner
{
private:
Monster* prototype;
public:
Spawner(Monster* p) : prototype(p) {}
virtual Monster* spawnMonster()
{
return prototype->clone();
}
}
// 이렇게 사용하면 된다.
Monster* ghostPrototype = new Ghost(5, 14);
Spawner* ghostSpawner = new Spawner(ghostPrototype);
ghostSpawner->spawnMonster();
- 함수를 만들어서 코드를 줄일 수도 있다.
Monster* spawnGhost()
{
return new Ghost();
}
typedef Monster* (*SpawnCallback)();
class Spawner
{
private:
SpawnCallback spawn;
public:
Spawner(SpawnCallback s) : spawn(s) {}
Monster* spawnMonster() { return spawn(); }
};
// 이렇게 사용하면 된다.
Spanwer* ghostSpawner = new Spawner(spawnGhost);
- 템플릿을 사용할 수도 있다.
class Spanwer
{
public:
virtual ~Spanwer() {}
virtual Monster* spawnMonster() = 0;
};
template<class T>
class SpawnerFor : public Spawner
{
public:
virtual Monster* spawnMonster() { return new T(); }
};
// 이렇게 사용하면 된다.
Spawner* ghostSpawner = new SpawnerFor<Ghost>();
개념적인 예시 #
public class Person
{
public int Age;
public DateTime BirthDate;
public string Name;
public IdInfo IdInfo;
// 얕은 복사
// string과 IdInfo가 제대로 복사되지 않는다.
public Person ShallowCopy()
{
return (Person) this.MemberwiseClone();
}
public Person DeepCopy()
{
Person clone = (Person) this.MemberwiseClone();
// 깊은 복사
clone.Name = String.Copy(Name);
clone.IdInfo = new IdInfo(IdInfo.IdNumber);
return clone;
}
}
public class IdInfo
{
public int IdNumber;
public IdInfo(int idNumber)
{
this.IdNumber = idNumber;
}
}
class Program
{
static void Main(string[] args)
{
Person p1 = new Person();
p1.Age = 42;
p1.BirthDate = Convert.ToDateTime("1977-01-01");
p1.Name = "Jack Daniels";
p1.IdInfo = new IdInfo(666);
Person p2 = p1.ShallowCopy(); // 얕은 복사
Person p3 = p1.DeepCopy(); // 깊은 복사
// p1이 변경됨
p1.Age = 32;
p1.BirthDate = Convert.ToDateTime("1900-01-01");
p1.Name = "Frank";
p1.IdInfo.IdNumber = 7878;
// 결과
DisplayValues(p1);
DisplayValues(p2); // IdInfo의 값이 p1과 동일하게 변경됨
DisplayValues(p3);
}
public static void DisplayValues(Person p)
{
Console.WriteLine("Name: {0:s}, Age: {1:d}, BirthDate: {2:MM/dd/yy}, ID#: {0:d}",
p.Name, p.Age, p.BirthDate, p.IdInfo.IdNumber);
}
}