Prototypes can also be used whenever you need classes that differ only in the type of processing they offer, for example in parsing of strings representing numbers in different radixes. This design pattern is not used very frequently.
The following is an example of Prototype Design Pattern:
//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Prototype is part of Creational Patterns
//Creational Patterns deal with initializing and configuring classes and objects
//Prototype creates a fully initialized instance to be copied or cloned
//We will take an example of creating Colour class.
//There are three parts to colour - Red, Green and Blue
//Simple colours like Red only contain the red component
//Complex colours like Angry and Peace contains all three components
#include <iostream>
#include <string>
#include <iomanip>
#include <map>
using namespace std;
//The abstract 'Protoype' class
class ColourPrototype
{
public:
virtual ColourPrototype* Clone(void) = 0;
};
//The 'ConcretePrototype' class
class Colour : public ColourPrototype
{
public:
Colour(int red, int green, int blue)
{
red_ = red, green_ = green, blue_ = blue;
}
ColourPrototype* Clone(void)
{
cout << "Cloning colour RGB: " << setw(3) << red_ << ", " << setw(3) << green_ <<", " << setw(3) << blue_ <<endl;
ColourPrototype* colourPrototype = new Colour(red_, green_, blue_);
return colourPrototype;
}
void SetRed(int red)
{
red_ = red;
}
void SetGreen(int green)
{
green_ = green;
}
void SetBlue(int blue)
{
blue_ = blue;
}
int GetRed(void)
{
return red_;
}
int GetGreen(void)
{
return green_;
}
int GetBlue(void)
{
return blue_;
}
private:
Colour(); //default constructor not allowed
int red_, green_, blue_;
};
//Prototype manager
class ColourManager
{
public:
virtual ~ColourManager()
{
while(!coloursMap_.empty())
{
map<string, ColourPrototype*>::iterator it = coloursMap_.begin();
delete it->second;
coloursMap_.erase(it);
}
}
void AddColour(const string& colour, ColourPrototype* prototype)
{
coloursMap_[colour] = prototype;
}
ColourPrototype* GetColour(const string& colour)
{
map<string, ColourPrototype*>::const_iterator it = coloursMap_.find(colour);
if(it != coloursMap_.end())
return it->second;
return NULL;
}
void PrintColours(void)
{
cout << "\nAvailable Colours and their values " << endl;
map<string, ColourPrototype*>::const_iterator it = coloursMap_.begin();
while(it != coloursMap_.end())
{
cout << setw(20) << it->first << " : ";
cout << setw(3) << dynamic_cast<Colour*>(it->second)->GetRed() << ", ";
cout << setw(3) << dynamic_cast<Colour*>(it->second)->GetGreen() << ", ";
cout << setw(3) << dynamic_cast<Colour*>(it->second)->GetBlue() << endl;
++it;
}
}
private:
map<string, ColourPrototype*> coloursMap_;
};
//The Main method
int main()
{
ColourManager* colourManager = new ColourManager();
//Add simple colours
colourManager->AddColour("Red", new Colour(255, 0, 0));
colourManager->AddColour("Green", new Colour(0, 255, 0));
colourManager->AddColour("Blue", new Colour(0, 0, 255));
//Add complex colours
colourManager->AddColour("Angry", new Colour(255, 54, 0));
colourManager->AddColour("Peace", new Colour(128, 211, 128));
colourManager->AddColour("Flame", new Colour(211, 34, 20));
//Clone existing colours, modify and add them to the manager
ColourPrototype* colour1 = (colourManager->GetColour("Red"))->Clone();
(dynamic_cast<Colour*>(colour1))->SetRed(200);
colourManager->AddColour("Light Red", colour1);
ColourPrototype* colour2 = (colourManager->GetColour("Peace"))->Clone();
(dynamic_cast<Colour*>(colour2))->SetRed(150);
(dynamic_cast<Colour*>(colour2))->SetBlue(150);
colourManager->AddColour("Extreme Peace", colour2);
ColourPrototype* colour3 = (colourManager->GetColour("Flame"))->Clone();
(dynamic_cast<Colour*>(colour3))->SetRed(255);
colourManager->AddColour("Hot Flame", colour3);
colourManager->PrintColours();
//clean the memory
delete colourManager;
return 0;
}
The output is as follows:
For more details, please see: http://www.dofactory.com/Patterns/PatternPrototype.aspx
Worst example you can think of:
ReplyDelete1. If my non-default ctor is not private then what's the point of asking object to clone itself. I need a scenario where in any case client has to ask factory/manager class for a clone. All the ctors should be private/protected.
2. A use of dynamic_cast<> , shows a bad design. Whenever there is a flaw in your design you need dynamic cast.
Couple of mistakes:
ReplyDelete1. There is no need in line "coloursMap_.erase(it);" in distractor of class ColourManager. The std::map will call distractor automatically to each element within, before the object of type ColourManager will get out of scope.
2. The "AddColour" function doesn't contain verification for attempt of repeated color insertion.