Many of the other answers here talk about the cost savings of cloning an already-configured object, but I would like to expand on the other "point" of the Prototype pattern. In some languages, where classes are treated as first-class objects, you can configure what type of object gets created by a client at runtime by simply passing it the class name. In languages like C++, where classes are not treated as first-class objects, the Prototype pattern allows you to achieve the same effect.
For example, let's say we have a Chef
in a restaurant whose job is to make and serve meals. Let's say the Chef
is underpaid and disgruntled, so he makes dishes like the following:
class Chef {
public:
void prepareMeal() const {
MozzarellaSticksWithKetchup* appetizer = new MozzarellaSticksWithKetchup();
// do something with appetizer...
HockeyPuckHamburgerWithSoggyFries* entree = new HockeyPuckHamburgerWithSoggyFries();
// do something with entree...
FreezerBurnedIceCream* dessert = new FreezerBurnedIceCream();
// do something with dessert...
}
};
Now let's say we want to change the Chef
to be an ostentatious celebrity chef. This means he/she has to new
different dishes in prepareMeal()
. We would like to modify the method so that the types of meals that get new
by the Chef
can be specified as parameters. In other languages where classes are first class objects, we can simply pass the class names as parameters to the method. We can't do this in C++, so we can benefit from the prototype pattern:
class Appetizer {
public:
virtual Appetizer* clone() const = 0;
// ...
};
class Entree {
public:
virtual Entree* clone() const = 0;
// ...
};
class Dessert {
public:
virtual Dessert* clone() const = 0;
// ...
};
class MozzarellaSticksWithKetchup : public Appetizer {
public:
virtual Appetizer* clone() const override { return new MozzarellaSticksWithKetchup(*this); }
// ...
};
class HockeyPuckHamburgerWithSoggyFries : public Entree {
public:
virtual Entree * clone() const override { return new HockeyPuckHamburgerWithSoggyFries(*this); }
// ...
};
class FreezerBurnedIceCream : public Dessert {
public:
virtual Dessert * clone() const override { return new FreezerBurnedIceCream(*this); }
// ...
};
// ...and so on for any other derived Appetizers, Entrees, and Desserts.
class Chef {
public:
void prepareMeal(Appetizer* appetizer_prototype, Entree* entree_prototype, Dessert* dessert_prototype) const {
Appetizer* appetizer = appetizer_prototype->clone();
// do something with appetizer...
Entree* entree = entree_prototype->clone();
// do something with entree...
Dessert* dessert = dessert_prototype->clone();
// do something with dessert...
}
};
Note that a clone()
method creates an instance of the derived type, but returns a pointer to the parent type. This means we can change the type of object that gets created by using a different derived type, and the client won't know the difference. This design now allows us to configure a Chef
-- the client of our Prototypes -- to make different types of dishes at runtime:
Chef chef;
// The same underpaid chef from before:
MozzarellaSticksWithKetchup mozzarella_sticks;
HockeyPuckHamburgerWithSoggyFries hamburger;
FreezerBurnedIceCream ice_cream;
chef.prepareMeal(&mozzarella_sticks, &hamburger, &ice_cream);
// An ostentatious celebrity chef:
IranianBelugaCaviar caviar;
LobsterFrittataWithFarmFreshChives lobster;
GoldDustedChocolateCupcake cupcake;
chef.prepareMeal(&caviar, &lobster, &cupcake);
You may wonder that used this way, the Prototype pattern buys you the same thing as the Factory Method pattern, so why not just use that? Because the Factory Method pattern would require a hierarchy of creator classes that mirror the hierarchy of products being created; i.e. we would need a MozzarellaSticksWithKetchupCreator
with a make()
method, a HockeyPuckHamburgerWithSoggyFriesCreator
with a make()
method, and so on. You could, therefore, view the Prototype pattern simply as one way to alleviate the code redundancy often introduced by the Factory Method pattern.
This argument is drawn from Design Patterns: Elements of Reusable Object-Oriented Software, a.k.a. the "Gang of Four" book.