83

So I'm learning about design patterns in school. Today I was told about the 'Prototype' design pattern.

I must be missing something, because I don't see the benefits from it. I've seen people online say it's faster than using new but this doesn't make sense; at some point, regardless of how the new object is created, memory needs to be allocated for it.

Doesn't this pattern run in the same circles as the 'chicken or egg' problem? Since the Prototype pattern essentially is just cloning objects, at some point the original object must be created itself (i.e. not cloned). This would mean that I need to have an existing copy of every object I want to clone already ready to clone?

Can anyone explain what the use of this pattern is?

1

9 Answers 9

60

The prototype pattern has some benefits, for example:

  • It eliminates the (potentially expensive) overhead of initializing an object
  • It simplifies and can optimize the use case where multiple objects of the same type will have mostly the same data

For example, say your program uses objects that are created from data parsed from mostley unchanging information retrieved over the network. Rather than retrieving the data and re-parsing it each time a new object is created, the prototype pattern can be used to simply duplicate the original object whenever a new one is needed.

Also, say that object may have data that uses up large amounts of memory, such as data representing images. Memory can be reduced by using a copy-on-write style inheritance, where the original, unduplicated data is shown until the code attempts to change that data. Then, the new data will mask to reference to the original data.

Sign up to request clarification or add additional context in comments.

3 Comments

Nitpicking (I gave a +1), but I think point #1 is a bit strongly worded. It potentially reduces the overhead of initializing an object. In many cases complex initialization code cannot be avoided, in which case the pattern buys you nothing. I'd also be reluctant to encourage the idea that a cloned object could share data with other clones. The typical idea is that clones should be independent, so unless you're careful you can easily break that relationship if you share (not copy) state amongst clones.
Good answer! Only your second use case violates the convention suggested in the Java API ("By convention, the object returned by this method should be independent of this object (which is being cloned).", see docs.oracle.com/javase/7/docs/api/java/lang/Object.html#clone())
The second point is solved with the Flyweight pattern, not Prototype.
45

The Prototype pattern is a creation pattern based on cloning a pre-configured object. The idea is that you pick an object that is configured for either the default or in the ballpark of some specific use case and then you clone this object and configure to your exact needs.

The pattern is useful to remove a bunch of boilerplate code, when the configuration required would be onerous. I think of Prototypes as a preset object, where you save a bunch of state as a new starting point.

7 Comments

'duck-typing and multiple inheritance on platforms that don't natively support such things'. Good point. But still leaves the annoying problem of needing an original object to clone.
So then, back to the original question, what's the point? How do you solve the chicken/egg problem? The GoF book describes the idea of a Prototype Manager, but fails to give enough concrete details on how it could possibly work (if a class is registered with the manager at runtime, then how can you avoid having clients of the manager do explicit casts on the returned cloned instance?)
@Mark, can you address the chicken-or-egg problem which seems to be the heart of the question?
Prototype is "Clone this object and make minor changes". It's true that you still have to create the original object. That's not what this creation method is trying to solve, rather it is addressing the case where you have a few highly configured objects that can serve as a starting point. Similar to a preset for a stereo or a synthesizer.
Further, there are languages (Javascript) where there is strictly speaking no other way to make what we normally consider to be an "object", i.e. a new / separate instance of encapsulated data married to accessors or methods.
|
16

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.

2 Comments

I fail to understand the point of this. prepareMeal already accepts pointers to base classes, so you can just pass any derived class instances you want to it. This seems totally unnecessary.
@palapapa Yes you could, but then you're changing the problem. The idea is that you have some code that creates things, and you want to configure that creation behavior. This is what creation patterns like Prototype try to solve. Perhaps the example would be more convincing if the prototypes were passed in the Chef constructor, saved as member variables, and then re-cloned on every call to prepareMeal()?
7

If you want to create an object but do not want to go through the expensive object creation procedure where network or database calls are made, then use the prototype pattern. Just create a copy of the object and do your changes on it.

Comments

2

using prototype pattern is completely depends on the your problem. In most of usual cases there is not any difference between cloning and creating new object. BUT if you are doing some complex or time consuming operations in constructor or when setting a property and it must do complex and time consuming operations , prototype pattern will help us. Because copying objects from old instance to new instance is easier and its performance is higher.(Deep cloning). So this pattern is more compatible with objects that their state is not changed for long time. Completely analyze your problem before using this pattern.

Comments

1

If you have a requirement, where you need to populate or use the same data containing Object repeatable

and

it is not possible to build from existing Object for example [ Building Object using Network Stream ] or

to build an Object is time-consuming [Building a Big Object, by getting data from Database] then use this design pattern, as in this a Copy the existing Object is created, this copy would be different from the Original Object and could be used just like Original one.

Comments

1

Prototype design pattern produces a cloneable object, can be used under following situations

1). when creating objects is expensive and the data held by the object wont go stale or stale data can be mitigated. the data held by the object might be queried from a database, or requested through http, or calculated with a long running CPU process, in such situations cloning an immutable object is beneficial but not at the expense of having stale data.

2). some times it might not be programmatically possible to recreate an object, like state objects or snapshots or DOM structures, you also wouldn't be able to mutate the original object because its shared and you cannot sensibly persist the object, in such situations cloning an immutable object is beneficial but not at the expense of having stale data.

3). you might want to have the exact copy of a dataset for testing on various different environments or when you need to have default values for object properties, cloning would help here.

Comments

0

Most of these answers are quite theoretical... I'd like to give a concrete example where the prototype pattern is so useful that it's obviously the best design choice without any comparable alternatives.

I am developing a Bayesian Deep Learning library. It is inspired by BayesianTorch's simple dnn_to_bnn() function/interface, which can convert certain neural networks into Bayesian ones. But only RNNs, CNNs and MLPs (not e.g. Transformers) are supported!

My version of dnn_to_bnn() is special because it fully embraces the prototype pattern. I don't need to manually implement numerous Bayesian sub-classes of common layer types. I just re-parameterize an already constructed model in-place. So I can support all possible neural network architectures with about 1/15th of the code BayesianTorch uses!!

And it gets better! In a Bayesian setting this pattern allows you to set your bayesian priors based on a pre-trained model's weights. Which is the only way I can think of that would make sense to use an informative prior in deep learning.

Comments

-1

Compared with the abstract factory pattern, by using the prototype pattern, you don't have to have a big factory hierarchy, just a big product hierarchy.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.