I am learning Prototype design pattern and get confused from an example represented on this article by sourcemaking.
class Stooge
{
public:
virtual void slap_stick() = 0;
virtual Stooge* clone() = 0;
};
class Larry : public Stooge
{
public:
void slap_stick()
{
cout << "Larry: poke eyes\n";
}
Stooge* clone() { return new Larry; }
};
class Moe : public Stooge
{
public:
void slap_stick()
{
cout << "Moe: slap head\n";
}
Stooge* clone() { return new Moe; }
};
class Curly : public Stooge
{
public:
void slap_stick()
{
cout << "Curly: suffer abuse\n";
}
Stooge* clone() { return new Curly; }
};
class Factory
{
public:
static Stooge* make_stooge( int choice );
private:
static Stooge* s_prototypes[4];
};
Stooge* Factory::s_prototypes[] = {0, new Larry, new Moe, new Curly};
Stooge* Factory::make_stooge( int choice )
{
return s_prototypes[choice]->clone();
}
int main()
{
vector roles;
int choice;
while (true)
{
cout << "Larry(1) Moe(2) Curly(3) Go(0): ";
cin >> choice;
if (choice == 0)
break;
roles.push_back(Factory::make_stooge( choice ) );
}
for (int i=0; i < roles.size(); ++i)
roles[i]->slap_stick();
for (int i=0; i < roles.size(); ++i)
delete roles[i];
}
According to description Prototype Design Pattern
According to this. The prototype design pattern is a design pattern that is used to instantiate a class by copying, or cloning, the properties of an existing object.
As far as I know, the normal way of copying a class is to use copy constructors, overload operator=, or implement clone function to instantiate a new object by copying all of the properties of an existing object.
On the example above, I do not see how it achieves creating new objects by copying the prototype, as nor copy constructors, neither overload operator=, or appropriate clone function defined.
So can I assume that this is not an implementation of prototype design pattern? Or maybe I am wrong on my assumptions and do not understand this example?
EDITED: As @songyuanyao mentioned
In the example, it's creating new objects newly, without copying anything
So I am considering above example as NOT appropriate example of prototype pattern as it DO NOT represent main target of prototype pattern.
First of all it is not necessary to clone an object calling a method on it, it could be perfectly possible you have a Manager
or Factory
that have a method used to clone the object i.e:
Object Factory::clone( Object &){
}
In addition, it is true you can use a Copy Constructor, but the downside of Copy Constructor is that it works only for Concrete Classes. If, for good design decisions, you offer only the object API (Interface/ Pure virtual) to users, then you cannot use a copy constructor on client side, so if the intended usage is cloning an item you can resolve that by adding a Clone
method
class BaseVirtualClass{
public:
virtual int foo() = 0;
virtual BaseVirtualClass * clone() = 0;
virtual ~BaseVirtualClass(){}
};
class DerivedClass: public BaseVirtualClass{
int state;
public:
DerivedClass(int a):state(a){}
DerivedClass( const DerivedClass & other)
: state(other.state){}
int foo(){ // override
return state;
}
BaseVirtualClass * clone(){ //override
// I'm using copy constructor here, but hidden from user
return new DerivedClass( *this);
}
};
usage:
BaseVirtualClass * obj = factory.createObject();
BaseVirtualClass * clone = obj->clone();
Also sometimes you want do that through factories:
BaseVirtualClass * obj = factory.createObject();
BaseVirtualClass * clone = factory.clone(obj);
NOTE: Apart fixing the missing copy constructor when you offer only pointers to abstract classes, the Clone
method have the clear intent to copy an object state creating a Deep Copy, without having to write boiler plate code to re-create the object in that particular state. When the responsibility of creating the object is not of the object (because you have to use a factory with a custom allocator or other complex dependencies) then the clone method is moved to the Factory.
EDIT:
The code sample the questioner found on internet seems to me an edge case. The objects are technically clones (they have no state, apart the type, and hence they have the same state), but I do not find that to be a good example of Prototype pattern: