In the first of this design patterns series, we looked at one of the creational design patterns, the singleton pattern. This week we’ll look at another creational pattern, the Prototype design pattern. Remember that creational patterns are concerned with the best way to create instances of objects.
The Gang of Four book defines the prototype pattern as follows:
“Specify the kind of objects to create using a prototypical instance and create new objects by copying this prototype”.
All prototype classes should have a common interface that makes it possible to copy objects even if their concrete classes are unknown. Prototype objects can produce full copies since objects of the same class can access each other’s private fields.
The prototype can be easily recognized by having a clone()
or copy()
method.
Prototype Pattern Usage
The prototype design pattern is used when object creation has a high cost in terms of time and resources, and when we already have a similar previously instantiated object.
When should we use the prototype pattern?
- When the classes are instantiated on the fly during runtime.
- When the cost of creating an object is expensive, resource-intensive or complicated.
- When we want to hide the complexities of creating objects.
The pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. Instead of creating new objects, we simply clone the prototypical instance. The prototype clones itself and creates a new object. The cloning isn’t done by any other class; rather the class itself provides the copying method. The clients of the class can create any number of new objects without knowing which type of object they will be.
Prototype Pattern: The Cloneable Interface
The prototype pattern is available in Java out of the box with the java.lang.Cloneable
interface. Any class can implement this interface by overriding the clone()
method.
When overriding the clone()
method, we need to decide between making a shallow copy or a deep copy. This is based on the actual system requirements, but is usually done as follows:
- If the class contains only primitive and immutable fields, we can do a shallow copy. This is a field by field copy – we copy the field value of the original object to the same field in the cloned object.
- If the class contains references to mutable fields, we should do a deep copy. A deep copy is when we make copies of all the objects that are referenced by the original object.
Cloning Mechanisms
We have a variety of coding mechanisms to clone an object:
- We can implement the
Cloneable
interface as explained earlier. - We can create a copy constructor. This is a constructor that takes in an object of the same class. THis will generally make a deep copy. We can even use the copy constructor in a
clone()
method by passing thethis
reference as the argument to the copy constructor. - We can use serialization and deserialization with a
ByteArrayInputStream
and aByteArrayOutputStream
.
Prototype Pattern: Example Code
Let’s say we’re busy creating our first person shooter game populated with assorted monsters. We’ve decided that we want to create a zombie horde as a challenge for the player. Obviously we could create a multitude of zombies by calling their constructors with the new
keyword.
Suppose we have to read data from our monster database before we can create a zombie. Then we have to modify this data in our game multiple times for each zombie. It won’t a good idea to create zombies using the new
keyword each time, and having to load the same or similar data again from the database. This would make the creation of a zombie horde very time-consuming.
A much better solution would be to create a single prototypical instance of a Zombie
, and then simply clone it over and over to create our fearsome horde of zombies.
To allow this cloning, we would implement the java.lang.Cloneable
interface:
public class Zombie implements java.lang.Cloneable { // other code @Override public Object clone() { // appropriate code } }
The clone()
method has protected
access in the Object
class. We would override this method with a public
method. The customary first step in any clone()
implementation is to invoke the superclass’s implementation of clone()
:
public Object clone() throws CloneNotSupportedException { «type» cloned = («type»)super.clone(); // type cast to correct class type cloned.mutableField = (MutableField) mutableField.clone(); cloned.immutableField = immutableField; cloned.primitiveField = primitiveField; ... return cloned; }
If we are writing a clone()
method for a direct subclass of Object
, we will need to either catch CloneNotSupportedException
or declare it in the throws
clause. The Object
class does a shallow field-by-field copy in low-level native code.
We could alternatively create a copy constructor, and call it from the overridden clone()
method. The requirements and mechanics of a copy constructor are possibly easier to understand than that of the clone()
method. Sample code follows:
public class Zombie implements java.lang.Cloneable { // other code public Zombie (Zombie zombie) { // appropriate code - easy to understand } @Override public Object clone() { return new Zombie(this); } }
As you see, the prototype pattern is a relatively simple pattern to understand and implement.
What’s next?
In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned!
As always, please share your comments and questions.