Reputation: 99
I am learning about design patterns and trying to implement them by myself. For most of design pattern I can understand there use and implementation but I am getting confused with Prototype pattern. Here is my approach to implement it.
Ship.h
class IShipPrototype
{
public:
virtual IShipPrototype* clone() = 0;
IShipPrototype(const std::string sName, float w = 10, float h = 10, float s = 10) : shipName{ sName }, width{ w }, height{ h }, speed{ s } {};
IShipPrototype(const IShipPrototype &that)
{
shipName = that.shipName;
width = that.width;
height = that.height;
speed = that.speed;
}
void Print()
{
std::cout << "-----------------------\n";
std::cout << "\tShip Info\t\n" <<
"Name:\t\t" << shipName << "\n" <<
"Width:\t\t" << width << "\n" <<
"Height:\t\t" << height << "\n" <<
"Speed:\t\t" << speed << std::endl;
}
void SetShipName(const std::string &newShipName) { shipName = newShipName; }
void SetShipHeight(float NewHeight) { height = NewHeight; }
void SetShipWidth(float NewWidth) { width = NewWidth; }
void SetShipSpeed(float NewSpeed) { speed = NewSpeed; }
private:
std::string shipName;
float width = 0, height = 0, speed = 0;
};
class Ship : public IShipPrototype
{
public:
Ship(const std::string& sName, float w, float h, float s) : IShipPrototype(sName, w, h, s) {}
Ship(const Ship &ship) : IShipPrototype(ship) {}
IShipPrototype* clone() override
{
return new Ship(*this);
}
};
class ShipFactory
{
public:
ShipFactory()
{
ships[0] = new Ship("titanic", 12, 43, 47);
ships[1] = new Ship("Black pearl", 15, 73, 24);
ships[2] = new Ship("Man O War", 32, 46, 14);
ships[3] = new Ship("Rose Marry", 24, 53, 52);
}
IShipPrototype* CreateCloneShip(int idx)
{
return ships[idx]->clone();
}
private:
std::map<int, IShipPrototype*> ships;
};
main.cpp
int main()
{
ShipFactory *factory = new ShipFactory();
IShipPrototype* titanicClone = factory->CreateCloneShip(0);
IShipPrototype* blackPearlClone = factory->CreateCloneShip(1);
IShipPrototype* manOwarClone = factory->CreateCloneShip(2);
IShipPrototype* roseMarry = factory->CreateCloneShip(3);
titanicClone->SetShipName("titanicClone");
titanicClone->SetShipHeight(100);
titanicClone->Print();
blackPearlClone->SetShipName("blackPearlClone");
blackPearlClone->SetShipSpeed(10);
blackPearlClone->Print();
manOwarClone->SetShipName("manOwarClone");
manOwarClone->SetShipWidth(40);
manOwarClone->Print();
roseMarry->SetShipName("roseMarry");
roseMarry->SetShipSpeed(130);
roseMarry->Print();
getchar();
return EXIT_SUCCESS;
}
Part which confuses me is that clone function returns the pointer for Interface, which means I can't store the clone in a different ship object, For eg. Ship* ship = factory->CreateCloneShip(0);
.
I thought the idea behind this design pattern is to create clone of a already existing object and then change some details of it.
Is it my implementation which is incorrect or I am missing something?
Upvotes: 4
Views: 911
Reputation: 24748
The Prototype Pattern allows us to create a copy of an object polymorphically by calling virtual member function, which is usually called clone(),
as in your code.
Part which confuses me is that clone function returns the pointer for Interface, which means I can't store the clone in a different ship object
In general, an overriding function must be precisely the same type as the type of the virtual function it overrides. However, if the virtual function to override returns a (raw) pointer or a reference, there is some constraint relaxation regarding the type the overriding function is allowed to return.
If that's the case, the return type of the overriding function may be a pointer or reference to a derived class of the class type to which the overridden function returns a pointer. This is known as covariant return type of a method.
Now, with this in mind and focusing on the IShipPrototype::clone()
virtual member function. It returns a IShipPrototype*
:
class IShipPrototype {
public:
virtual IShipPrototype* clone() = 0; // returns IShipPrototype*
// ...
};
Since IShipPrototype
is a public base of Ship
, an overriding Ship::clone()
can return Ship*
instead of IShipPrototype*
:
class Ship: public IShipPrototype {
public:
Ship* clone() override; // returns Ship*
// ...
};
This way, if you have a Ship
object and you call clone()
directly on it, you obtain a Ship*
, not an IShipPrototype*
– though Ship*
does implicitly convert to IShipPrototype*
because IShipPrototype
is a public base class of Ship
. IShipPrototype
and Ship
here are said to be covariant types.
Note that if you call clone()
through the IShipPrototype
interface, the static type of the type the returned pointer points to is IShipPrototype*
. The dynamic type, however, will be Ship
if it was called on a Ship
instance.
Upvotes: 3