Tuesday, 17 August 2010

C++ example for Prototype Design Pattern

The Protoype pattern is used when creating an instance of a class is very time-consuming or complex in some way. Then, rather than creating more instances, you make copies of the original instance, modifying them as appropriate.


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:



Other good example of Prototype is available here and here.

2 comments:

  1. Worst example you can think of:
    1. 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.

    ReplyDelete
  2. Couple of mistakes:
    1. 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.

    ReplyDelete