C++ - Design pattern - Prototype

The Prototype design pattern is used in order to clone an object.

Main goal to prototype design pattern is to create new objects with default values.

This cloning operation is actually a copy of an object and can be made with a shallow or a deep copy.

Let's see that in this Prototype design pattern tutorial.

First of all

Find the complete code on GitHub

There are two different ways to copy an object: shallow and deep.

The former transfers the data in the copied object, so each change in the source object pointers will be impacted into the new created object.

They are quite linked and pointers data members share the same memory addresses.

The latter copies data into the new object but creates a new memory address for each pointer data member.

In this case,  once created the new copied object will be totally independent from its clone, data changed in the source won't be applied to the new copied object.

By default the compiler calls the copy constructor and makes a shallow copy of a new same object.

In order to accomplish a deep copy, it will be necessary to overload some operators and manage it ourself.

Well this said, we're going to use the shallow copy in this tutorial because our ships have only data member that don't require pointers.

The core of the Prototype design pattern is to use a classic map to stock objects already created (with an init method).

So instead of create a new object each time we need one, we will simply use this object that was already stocked in our map.

For example we want to create a Tiny ship, at first use we create it and we stock it.

Once we finish using this object, we won't delete it from the map.

Next time we will want to add another Tiny ship on the screen we won't have to create it again because we already have it.

So we will only call the ship from the map without recreate it.

Simple and efficient.

And we do that for every kind of type of ship we want to create: Tiny, Normal or Enormous.

The Prototype design pattern has to be seen as a default state for a particular kind of object.

In our example below, we're going to create 3 different ships with default values.

As we want to use these default values for every new ships added on our screen, so we won't initialize them more than once.

We're going to create new objects by copying their respective prototype.

Code

IPrototype.h

#ifndef __BADPROG_IPROTOTYPE_H__
#define __BADPROG_IPROTOTYPE_H__

// Badprog.com

#include <memory>

//-----------------------------------------------------------------------------
// IPrototype.
//-----------------------------------------------------------------------------
class IPrototype
{
public:
  virtual ~IPrototype() {}
  virtual std::unique_ptr<IPrototype> clone() = 0;
  virtual const double getWidth() const       = 0;
  virtual const double getHeight() const      = 0;
  virtual const double getSpeed() const       = 0;
  virtual void setWidth(const double &w)      = 0;
  virtual void setHeight(const double &h)     = 0;
  virtual void setSpeed(const double &s)      = 0;

protected:
    double _width;
    double _height;
    double _speed;
};

#endif // __BADPROG_IPROTOTYPE_H__

 

PShipEnormous.cpp

// Badprog.com

#include <memory>
#include "PShipEnormous.h"

//-----------------------------------------------------------------------------
// Constructor.
//-----------------------------------------------------------------------------
PShipEnormous::PShipEnormous() {
setWidth(300);
setHeight(700);
setSpeed(50);
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
PShipEnormous::~PShipEnormous() {
}

//-----------------------------------------------------------------------------
// Clone.
//-----------------------------------------------------------------------------
std::unique_ptr<IPrototype> PShipEnormous::clone() {
return std::make_unique<PShipEnormous> (*this);
}

//-----------------------------------------------------------------------------
// getWidth.
//-----------------------------------------------------------------------------
const double PShipEnormous::getWidth() const {
return this->_width;
}

//-----------------------------------------------------------------------------
// getHeight.
//-----------------------------------------------------------------------------
const double PShipEnormous::getHeight() const {
return this->_height;
}

//-----------------------------------------------------------------------------
// getSpeed.
//-----------------------------------------------------------------------------
const double PShipEnormous::getSpeed() const {
return this->_speed;
}

//-----------------------------------------------------------------------------
// setWidth.
//-----------------------------------------------------------------------------
void PShipEnormous::setWidth(const double &w) {
this->_width = w;
}

//-----------------------------------------------------------------------------
// setHeight.
//-----------------------------------------------------------------------------
void PShipEnormous::setHeight(const double &h) {
this->_height = h;
}

//-----------------------------------------------------------------------------
// setSpeed.
//-----------------------------------------------------------------------------
void PShipEnormous::setSpeed(const double &s) {
this->_speed = s;
}

 

PShipEnormous.h

#ifndef __BADPROG_PSHIPENORNOUS_H__
#define __BADPROG_PSHIPENORNOUS_H__

// Badprog.com

#include <memory>
#include "IPrototype.h"

//-----------------------------------------------------------------------------
// PShipEnormous.
//-----------------------------------------------------------------------------
class PShipEnormous : public IPrototype
{
public:
  PShipEnormous();
  virtual ~PShipEnormous();

  virtual const double getWidth() const override;
  virtual const double getHeight() const override;
  virtual const double getSpeed() const override;
  virtual void setWidth(const double &w) override;
  virtual void setHeight(const double &h) override;
  virtual void setSpeed(const double &s) override;
  std::unique_ptr<IPrototype> clone() override;

};

#endif // __BADPROG_PSHIPENORNOUS_H__

 

PShipFactory.cpp

#include "PShipTiny.h"
#include "PShipNormal.h"
#include "PShipEnormous.h"
#include "PShipFactory.h"

//-----------------------------------------------------------------------------
// Constructor.
//-----------------------------------------------------------------------------
PShipFactory::PShipFactory() {
prototypeInit();
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
PShipFactory::~PShipFactory() {
}

//-----------------------------------------------------------------------------
// Initialization of every prototype.
//-----------------------------------------------------------------------------
void PShipFactory::prototypeInit(void) {
mapOfPrototypes[SHIP_TINY] = std::make_unique<PShipTiny>();
mapOfPrototypes[SHIP_NORMAL] = std::make_unique<PShipNormal>();
mapOfPrototypes[SHIP_ENORMOUS] = std::make_unique<PShipEnormous>();
}

//-----------------------------------------------------------------------------
// retrieveShip.
//-----------------------------------------------------------------------------
std::unique_ptr<IPrototype> PShipFactory::retrieveShip(SHIP_TYPE shipType) {
return mapOfPrototypes[shipType]->clone();
}

 

PShipFactory.h

#ifndef __BADPROG_PSHIPFACTORY_H__
#define __BADPROG_PSHIPFACTORY_H__

// Badprog.com

#include <memory>
#include "IPrototype.h"
#include <map>

//-----------------------------------------------------------------------------
// PShipFactory.
//-----------------------------------------------------------------------------
class PShipFactory
{
public:
  enum SHIP_TYPE { SHIP_TINY, SHIP_NORMAL, SHIP_ENORMOUS };

  PShipFactory();
  ~PShipFactory();

  void prototypeInit(void);
  std::unique_ptr<IPrototype>retrieveShip(SHIP_TYPE type);

private:
    std::map<SHIP_TYPE, std::unique_ptr<IPrototype>> mapOfPrototypes;
};

#endif // __BADPROG_PSHIPFACTORY_H__

 

PShipNormal.cpp

// Badprog.com

#include <memory>
#include "PShipNormal.h"

//-----------------------------------------------------------------------------
// Constructor by default.
//-----------------------------------------------------------------------------
PShipNormal::PShipNormal() {
setWidth(200);
setHeight(400);
setSpeed(250);
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
PShipNormal::~PShipNormal() {
}

//-----------------------------------------------------------------------------
// Clone.
//-----------------------------------------------------------------------------
std::unique_ptr<IPrototype> PShipNormal::clone() {
return std::make_unique<PShipNormal> (*this);
}

//-----------------------------------------------------------------------------
// getWidth.
//-----------------------------------------------------------------------------
const double PShipNormal::getWidth() const {
return this->_width;
}

//-----------------------------------------------------------------------------
// getHeight.
//-----------------------------------------------------------------------------
const double PShipNormal::getHeight() const {
return this->_height;
}

//-----------------------------------------------------------------------------
// getSpeed.
//-----------------------------------------------------------------------------
const double PShipNormal::getSpeed() const {
return this->_speed;
}

//-----------------------------------------------------------------------------
// setWidth.
//-----------------------------------------------------------------------------
void PShipNormal::setWidth(const double &w) {
this->_width = w;
}

//-----------------------------------------------------------------------------
// setHeight.
//-----------------------------------------------------------------------------
void PShipNormal::setHeight(const double &h) {
this->_height = h;
}

//-----------------------------------------------------------------------------
// setSpeed.
//-----------------------------------------------------------------------------
void PShipNormal::setSpeed(const double &s) {
this->_speed = s;
}

 

PShipNormal.h

#ifndef __BADPROG_PSHIPNORMAL_H__
#define __BADPROG_PSHIPNORMAL_H__

// Badprog.com

#include <memory>
#include "IPrototype.h"

//-----------------------------------------------------------------------------
// PShipNormal.
//-----------------------------------------------------------------------------
class PShipNormal : public IPrototype
{
public:
  PShipNormal();
  virtual ~PShipNormal();

  virtual const double getWidth() const override;
  virtual const double getHeight() const override;
  virtual const double getSpeed() const override;
  virtual void setWidth(const double &w) override;
  virtual void setHeight(const double &h) override;
  virtual void setSpeed(const double &s) override;
  std::unique_ptr<IPrototype> clone() override;

};

#endif // __BADPROG_PSHIPNORMAL_H__

 

PShipTiny.cpp

// Badprog.com

#include <memory>
#include "PShipTiny.h"

//-----------------------------------------------------------------------------
// Constructor by default.
//-----------------------------------------------------------------------------
PShipTiny::PShipTiny() {
setWidth(50);
setHeight(100);
setSpeed(500);
}

//-----------------------------------------------------------------------------
// Destructor.
//-----------------------------------------------------------------------------
PShipTiny::~PShipTiny() {
}

//-----------------------------------------------------------------------------
// Clone.
//-----------------------------------------------------------------------------
std::unique_ptr<IPrototype> PShipTiny::clone() {
return std::make_unique<PShipTiny> (*this);
}

//-----------------------------------------------------------------------------
// getWidth.
//-----------------------------------------------------------------------------
const double PShipTiny::getWidth() const {
return this->_width;
}

//-----------------------------------------------------------------------------
// getHeight.
//-----------------------------------------------------------------------------
const double PShipTiny::getHeight() const {
return this->_height;
}

//-----------------------------------------------------------------------------
// getSpeed.
//-----------------------------------------------------------------------------
const double PShipTiny::getSpeed() const {
return this->_speed;
}

//-----------------------------------------------------------------------------
// setWidth.
//-----------------------------------------------------------------------------
void PShipTiny::setWidth(const double &w) {
this->_width = w;
}

//-----------------------------------------------------------------------------
// setHeight.
//-----------------------------------------------------------------------------
void PShipTiny::setHeight(const double &h) {
this->_height = h;
}

//-----------------------------------------------------------------------------
// setSpeed.
//-----------------------------------------------------------------------------
void PShipTiny::setSpeed(const double &s) {
this->_speed = s;
}

 

PShipTiny.h

#ifndef __BADPROG_PSHIPTINY_H__
#define __BADPROG_PSHIPTINY_H__

// Badprog.com

#include <memory>
#include "IPrototype.h"

//-----------------------------------------------------------------------------
// PShipTiny.
//-----------------------------------------------------------------------------
class PShipTiny : public IPrototype
{
public:
  PShipTiny();
  virtual ~PShipTiny();
 
  const double getWidth() const override;
  const double getHeight() const override;
  const double getSpeed() const override;
  void setWidth(const double &w) override;
  void setHeight(const double &h) override;
  void setSpeed(const double &s) override;
  std::unique_ptr<IPrototype> clone() override;

};

#endif // __BADPROG_PSHIPTINY_H__

 

Main.cpp

// Badprog.com

#include <iostream>

#include "PShipTiny.h"
#include "PShipNormal.h"
#include "PShipEnormous.h"
#include "PShipFactory.h"

//-----------------------------------------------------------------------------
// Main.
//-----------------------------------------------------------------------------
int main()
{
PShipFactory factory;

auto ship1 = factory.retrieveShip(PShipFactory::SHIP_TINY);
auto ship2 = factory.retrieveShip(PShipFactory::SHIP_NORMAL);
auto ship3 = factory.retrieveShip(PShipFactory::SHIP_ENORMOUS);
auto ship4 = factory.retrieveShip(PShipFactory::SHIP_ENORMOUS);
auto ship5 = factory.retrieveShip(PShipFactory::SHIP_ENORMOUS);
auto ship6 = factory.retrieveShip(PShipFactory::SHIP_ENORMOUS);

std::cout << "Address 1 = " << &ship1 << std::endl;
std::cout << "Width = " << ship1->getWidth() << std::endl;
std::cout << "Height = " << ship1->getHeight() << std::endl;
std::cout << "Speed = " << ship1->getSpeed() << std::endl;
 
std::cout << "Address 2 = " << &ship2 << std::endl;
std::cout << "Width = " << ship2->getWidth() << std::endl;
std::cout << "Height = " << ship2->getHeight() << std::endl;
std::cout << "Speed = " << ship2->getSpeed() << std::endl;
 
std::cout << "Address 3 = " << &ship3 << std::endl;
std::cout << "Width = " << ship3->getWidth() << std::endl;
std::cout << "Height = " << ship3->getHeight() << std::endl;
std::cout << "Speed = " << ship3->getSpeed() << std::endl;
 
std::cout << "Address 4 = " << &ship4 << std::endl;
std::cout << "Width = " << ship4->getWidth() << std::endl;
std::cout << "Height = " << ship4->getHeight() << std::endl;
std::cout << "Speed = " << ship4->getSpeed() << std::endl;
 
std::cout << "Address 5 = " << &ship5 << std::endl;
std::cout << "Width = " << ship5->getWidth() << std::endl;
std::cout << "Height = " << ship5->getHeight() << std::endl;
std::cout << "Speed = " << ship5->getSpeed() << std::endl;
 
std::cout << "Address 6 = " << &ship6 << std::endl;
std::cout << "Width = " << ship6->getWidth() << std::endl;
std::cout << "Height = " << ship6->getHeight() << std::endl;
std::cout << "Speed = " << ship6->getSpeed() << std::endl;

return 0;
}

Compiling, linking, executing

g++ *.cpp -o go.exe -std=c++1y ; ./go.exe

Result

Address 1 = 0133FA7C

Width = 50

Height = 100

Speed = 500

Address 2 = 0133FA70

Width = 200

Height = 400

Speed = 250

Address 3 = 0133FA64

Width = 300

Height = 700

Speed = 50

Address 4 = 0133FA58

Width = 300

Height = 700

Speed = 50

Address 5 = 0133FA4C

Width = 300

Height = 700

Speed = 50

Address 6 = 0133FA40

Width = 300

Height = 700

Speed = 50

Conclusion

As we can see the addresses of all our ships are different, even those created for the 4 ShipEnormous.

Thus it was the result expected with the exact same values copied from the prototype ShipEnormous already created in the factory.

Good job guys, once again you did it! cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.