Skip to main content

[Design Pattern] 행동 패턴 5. 프로토타입 (Prototype)




프로토타입 (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);
  }
}



References #