As we said, the biggest benefit of using the Prototype pattern is that we can make copies without knowing the type. This means we can make a copy of a function parameter or function return type without caring about the derived class involved. This also means that we can share a pointer with another class or method and we don't need to care if the type is modified.
In the following example, we have a SpawnShape class with the ability to spawn a specific type of shape. By using the Prototype pattern, the class doesn't need to care what type it is spawning. The constructor takes a pointer to some shape and then it just needs to call the Clone method. If the base class pointer is pointing to a Circle, then a circle will be created. However, if we have a pointer to a Triangle, then a triangle will be created. Here is an example that shows the Prototype pattern in action:
class ShapeSpawner
{
public:
ShapeSpawner (Shape* pShape, float maxTime):
m_pToSpawn(pShape),
m_spawnTime(0.f),
m_maxSpawnTime(maxTime)
{
}
void Update(float dt)
{
m_spawnTime += dt;
if(m_spawnTime > m_maxSpawnTime)
{
//The class doesn't care what type it is cloning
Shape* pClone = m_pToSpawn->Clone();
//...Register the clone somehow
//Reset timer
m_spawnTime = 0;
}
}
private:
Shape* m_pToSpawn;
float m_spawnTime;
float m_maxSpawnTime;
};
Our SpawnShape class doesn't care if it is spawning a Circle, Square, or Triangle, or any new shape we might create later. It can make copies without knowing the real type of the shape. If we add a public SetShape method, we could even change the type that spawns at runtime. Compare this with a more rigid example that can only spawn Circles:
class CircleSpawner
{
public:
CircleSpawner (Circle* pCircle, float maxTime):
m_pToSpawn(pCircle),
m_spawnTime(0.f),
m_maxSpawnTime(maxTime)
{
}
void Update(float dt)
{
m_spawnTime += dt;
if(m_spawnTime > m_maxSpawnTime)
{
//Use copy constructor
Circle* pClone = new Circle(*m_pToSpawn);
//...Register the clone somehow
//Reset timer
m_spawnTime = 0;
}
}
private:
Circle* m_pToSpawn;
float m_spawnTime;
float m_maxSpawnTime;
};
In the second example (not using the Prototype pattern) we are forced to use the copy constructor of the derived class, in this case the Circle. If we want to spawn a Square or a Triangle, we would need to create a SquareSpawner or TriangleSpawner. That is a lot of repeated code. It could get worse as we add even more shapes. By using the Prototype pattern, we can reduce the number of classes we need.