The prototype creational pattern is great for creating objects that you may not know about ahead of time. If your program allows users to build complex objects from simpler components and then needs to build more of these complex objects or if your program loads plugins at runtime, then it can benefit from this design pattern.
In order to understand and get the most benefit from this pattern, you need to understand the difference between a shallow copy and a deep copy. Shallow vs. deep copies are normally discussed when working with classes that have pointers or references especially to other class types which could also have pointers and references. A shallow copy just copies the first class data members as-is so that any pointers in the copy will point to the same data as in the original. A deep copy will change pointers so they point to their own copy of whatever the original points to. Deep copies will also continue this pattern looking for any possibility of shared data.
To make use of the prototype design pattern, just create a base class with a virtual method called clone which returns a pointer to another base class. The clone method will implement a deep copy and return a pointer to the base class. Your original code will just work with the base class pointer without worrying about what specific type it actually points to. After all, this type could be some new type defined by a plugin or some add-on which your code has no idea even exists.
Listen to the full episode for more about this pattern or read the full transcript below.
The basic description says that this pattern allows you to create new objects by copying prototypes or examples. Before we get too far into this explanation, I want to explain an important concept that’s directly applicable to this pattern as well as any time you need to copy items.
This concept is the difference between a shallow copy and a deep copy. And sometimes, there’s no difference at all. Let’s take a simple example and say you just have an integer variable called height with the value 1. You can create another integer variable called anotherHeight and assign it whatever value is in the height variable. When you’re done, both variables will contain the value 1. There’s no need to consider a shallow vs. deep copy because it just doesn’t apply. You either assign the value from another integer or you don’t.
The really important part about this assignment is that while both height and anotherHeight have the value 1, you can change anotherHeight to 3 without affecting the height variable. Even if they have the same value, they have their own independent values.
Let’s take a more complicated example and look at a class with a couple integer member variables height and width. You have a class with either your own custom assignment operator and copy constructor or you’re using the default. Let’s call this class a box and you have an instance called redBox that’s 3 wide and 5 tall. The units are up to you. These could be feet, inches, meters, or whatever. You have another box called the blueBox that you copy from the redBox. When you’re done, both boxes will have their own width and height that’s the same. They’re also independent of one another.
But what about a pointer or a reference? If you have an integer variable called height with the value 1 and then you declare a pointer to an integer variable called heightPointer and assign heightPointer the address of your height integer, then heightPointer points to height. Now you already know that there’s really only a single integer value in memory here with the value 1. The other pointer variable just points to the same memory. Changing height to the value 3 will also change the value that the heightPointer variable points to. This is because it points to the same thing. But what if we have another pointer variable called anotherHeightPointer and assign it a copy of heightPointer? All we really have now are two pointers that both point to the same integer in memory. There’s still just the single integer value that now has the value 3. You can copy pointers all you want to another pointer and all you get is that they all point to the same thing.
This is called a shallow copy. If you want a deep copy, well, then you’re going to need to go deeper. To make a deep copy of heightPointer, you need to first make a copy of the actual integer that heightPointer points to. This will give you two separate integer values in memory that initially have the same value. Then you create the anotherHeightPointer and set it to point to this new integer. When you’re done, heightPointer and anotherHeightPointer will no longer point to the same memory. They’ll point to their own separate integers. Deep copies end up changing pointer values during the copy so they have their own values. If you copy the pointers as is, then they point to the same thing and you have a shallow copy.
Now, you don’t normally refer to shallow vs. deep copies with just a single pointer copy like this but you can if you want to be explicit. Shallow vs. deep copies are normally discussed when they apply to classes that contain pointers or references. In other words when there’s the possibility that copying an instance will result in both classes referring to the same member data.
A deep copy can’t just stop at the first pointer it finds either. Because you can have a class with a member pointer that points to another class type which could also have pointers. To perform a deep copy, you have to duplicate the entire structure. If you don’t then you may end up with some portion of the class data shared between the copy and the original.
Alright, enough background. I’ll explain what all this has to do with the prototype pattern right after this message from our sponsor.
( Message from Sponsor )
Back to the prototype pattern. This pattern is really good at creating instances of classes you may not have even heard about when your application was written. Why would you ever need to create something that doesn’t exist when you’re writing the code in the first place?
Let’s say that your application lets users customize inventory items and then combine them in interesting ways to create something unique. Then if the user can save this creation so that it can be used again later, you’re going to probably want that later use to have its own values. The custom creation has effectively become part of your program and is used to build yet more custom items. You need some way to create these new items but they didn’t exist in the original program. How can you create something when you don’t know what you’re creating? That’s not quite accurate. Because you know what you need to create. You just need to copy what’s already there.
Another example is maybe your application allows other developers to add-on to it and extend it in ways you never thought about originally. If you want to allow users to make use of these add-ons, then they’re probably going to expect to be able to work with multiple instances of each add-on. Maybe this is an adventure game and some other developer adds a new monster type. You’ll want the hero to fight several of these new monsters and a really good way to do that is to copy new monsters from the one that was created by the developer.
But this still hasn’t answered the question of how do you go about copying something when you have no idea what you’re copying. The answer is simple. You don’t. What you do instead is declare a base class that any add-ons or user creations will derive from and in the base class, declare a virtual method called clone.
For user created items, the use isn’t really programming new code, just changing some values and combining existing or other user created content into new combinations. All you need to do is make sure that your initial classes that derive from your base class implement the clone method with a deep copy. And for add-ons that another programmer provides such as a completely new type of monster, the developer will be responsible for implementing the clone method to perform the deep copy.
Your original code just works with the base class clone method which returns a full deep copy as another pointer to the base class. You just work with these copies as if they are base classes and don’t need to worry at all about what specific type or combination of types they represent.
I’ll also mention that just like the user might be allowed to build new types by changing values and combining types, you can do the same thing. If you have a bunch of classes that are all similar, then consider making them just a single class with some data members that control the differences. Then you can store what would have been various classes as a data file that you can read from disk to construct them. You can call on the same clone method to create a new instance which you can then customize with its own data.