Prototype
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying the implemented prototype.
Concept
In general creating a new object from scratch is a costly operation. When multiple instances of same type has be created, it would be quick to create those instances by cloning or copying the existing prototype instance.
Note:
Consider the
Object.clone()
method as an example of prototype.
Analogy
Suppose we have a master copy of a document, and we need to add some more content to the document. We can make photocopy of the original document and add extra content to it with creating a new document from scratch. Because creating document from scratch is a tedious task and so photocopy comes to rescue.
Implementation In Java
In java, prototype pattern can be implemented using the Object.clone()
method. And the cloneable class must implement the interface Cloneable
Other ways to create copy the object is using serialization mechanism or by creating your own copy constructor and use it.
There are two types of copy in java
Shallow Copy
A shallow copy creates a new object and then copies various field values from the original object to the new object. It is also known as a field-by-field
copy. If the original object contains any references to other objects as fields, then the references of those objects are copied into the new object, (i.e., you do not create the copies of those objects).
Let’s take an example to understand the underlying mechanism.
Suppose there is an object J, it has reference to other object A, and object A has reference to object B as shown below
flowchart LR
objectJ((object J))
objectA((object B))
objectC((object C))
objectJ --> objectA --> objectC
Let’s say, shallow copy of object J is Object K. Now, the reference of A is copied into object K as depicted in below
flowchart LR
objectJ((object J))
objectK((object K))
objectA((object B))
objectC((object C))
objectJ --> objectA --> objectC
objectK --> objectA
Deep Copy
In a deep copy, the new object is totally separated from the original one. Any changes made in one object should not be reflected on the other one. To create a deep copy in Java, you may need to override the clone()
method and then proceed. Also, a deep copy is expensive because you need to create additional objects.
So, from the shallow copy example, object K will have copy of data and with new references.
Class Diagram
Following is the Class diagram for prototype pattern implementation.
classDiagram
class Cloneable {
<<interface>>
}
class BasicCar {
<<abstract>>
+String modelName
+double onRoadPrice
+BasicCar()
+setModelName(String setModelName)
+setOnRoadPrice(double onRoadPrice)
+clone() BasicCar
}
class Jaguar {
+Jaguar()
+clone() BasicCar
}
class Ford {
+Ford()
+clone() Ford
}
class Client {
+process()
}
Client --> BasicCar
Cloneable <|.. BasicCar: implements
BasicCar <|-- Jaguar: extends
BasicCar <|-- Ford: extends
Illustration
BasicCar
is a prototype class, and it implementsCloneable
marker interface.- Whichever class extends
BasicCar
should overrideclone()
method to create a shallow copy ofBasicCar
. - Client uses the prototype class to create objects using
clone()
method.
Prototype Class Implementation
As the BasicCar
implements Cloneable
marker interface, it has to implement clone()
method and method throws CloneNotSupportedException
BasicCar.java
abstract class BasicCar implements Cloneable {
// declare attributes
public String modelName;
public double onRoadPrice;
// constructor
public BasicCar() {}
// set modelName
public void setModelName(String modelName) {
this.modelName = modelName;
}
// set onRoadPrice
public void setOnRoadPrice(double onRoadPrice) {
this.onRoadPrice = onRoadPrice;
}
// clone method
public BasicCar clone() throws CloneNotSupportedException {
return (BasicCar) super.clone();
}
}
Sub-Classes Implementation
Subclasses which extends the BasicCar overrides clone()
method.
Jaguar.java
class Jaguar extends BasicCar {
// constructor
public Jaguar() {
super();
}
@Override // override clone method
public BasicCar clone() throws CloneNotSupportedException {
return super.clone();
}
// class specific methods
public void doSomething() {
// do something
}
}
Ford.java
class Ford extends BasicCar {
// constructor
public Ford() {
super();
}
@Override // override clone method
public BasicCar clone() throws CloneNotSupportedException {
return super.clone();
}
// class specific methods
public void doSomething() {
// do something
}
}
Client Implementation
Clint creates BasicCar objects and clone the existing objects to create new objects.
Client.java
class Client {
public void process() throws CloneNotSupportedException {
// create a basic jaguar object
BasicCar ePase = new Jaguar();
ePase.setModelName("E-Pase");
ePase.setOnRoadPrice(100_000);
// create new clone jaguar object
BasicCar fPase = ePase.clone();
fPase.setModelName("F-Pase");
fPase.setOnRoadPrice(200_000);
// create a basic Ford Object
BasicCar endeavour = new Ford();
endeavour.setModelName("Endeavour");
endeavour.setOnRoadPrice(50_000);
// create new clone ford object
BasicCar ecoSport = endeavour.clone();
ecoSport.setModelName("Endeavour");
ecoSport.setOnRoadPrice(50_000);
}
}
Advantages
- It is useful when the creation of the class is expensive and complicated process.
- It helps to focus on key activities only leaving complex object creation.
- Helps to include or discard products at realtime
Disadvantages
- Every subclass that extends the prototype class needs to implement the
clone()
method or copying mechanism. - Sometimes creating a copy of the object is not simple if the objects does not support cloning or if there are circular references.
- In Java, a class with
clone()
method needs to implementCloneable
interface, else it will throwCloneNotSupportedException
.
When To Use
- When the object creation is expensive and may have to create collection of same object types.
- Avoid having too many classes that have few variants of state.
- Avoid parallel hierarchy of factories for your product classes.
- Dynamically load classes in the application
- Achieve loose coupling between the classes
Known Uses
- Certain languages have inbuild support for cloning the either through language features or frameworks.
- Used mostly in CAD/CAM applications, games, etc.
Java
- A simple example of prototype pattern used in java is
java.lang.Object.clone()
Related Patterns
- Designs that use
Composite Pattern
andDecorator Pattern
can benefit fromPrototype pattern heavily
. - Prototype pattern is unique among the other patterns as it needs an object to create new instances while other patterns rely on classes to create new instances.
Abstract Factory
can be implemented by using Prototype Pattern instead of aFactory Method
Prototype Pattern
requires initialization but does not require sub-typing whileFactory Method Pattern
andAbstract Factory Pattern
requires sub-typing but does not need to initialize.
Learn other design patterns