Prototype design Pattern
Prototype design pattern is a creational design pattern that allow to create new object by cloning an existing object.It is helpful when creating new objects is expensive or when you want to isolate the object creation process from the client code.
Enough of definition now lets understand with an example
Let’s say you want to create a car game where you required multiple cars. now you have a car object that is expensive to create and has a complex initialisation process. In this case, You can use the Prototype Design Pattern to create new car objects by cloning the existing one.
you would create a Car class with all the required properties and methods. Then, you would implement a clone() method that creates a new instance of the Car class and copies all the properties of the existing car instance into it.
Let’s have a look at the code
public abstract class Vehicle implements Cloneable {
private String model;
private int year;
private String color;
public Vehicle(String model, int year, String color) {
this.model = model;
this.year = year;
this.color = color;
}
public String getModel() {
return model;
}
public int getYear() {
return year;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Car extends Vehicle {
private int numDoors;
public Car(String model, int year, String color, int numDoors) {
super(model, year, color);
this.numDoors = numDoors;
}
public int getNumDoors() {
return numDoors;
}
public void setNumDoors(int numDoors) {
this.numDoors = numDoors;
}
}
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Car car1 = new Car("XYZ", 2022, "Yellow", 1);
Car car2 = (Car) car1.clone();
System.out.println("Car 1: " + car1.getMake() + " " + car1.getModel() + " " + car1.getYear() + " " + car1.getColor() + " " + car1.getNumDoors());
System.out.println("Car 2: " + car2.getMake() + " " + car2.getModel() + " " + car2.getYear() + " " + car2.getColor() + " " + car2.getNumDoors());
car2.setColor("Pink");
System.out.println("Car 1 color: " + car1.getColor());
System.out.println("Car 2 color: " + car2.getColor());
}
}In this, Vehicle class defines the common properties and methods including the clone method that will create the clone of object. (using Cloneable interface). Now we have a Car class which extend the properties and method of vehicle and add numDoors property.
In the main method we are simply creating object of Car car1 and and create clone of the car2 by calling clone() method. this will have all the properties what car1 has now we can change accordingly in cloned object.
Shallow copy and Deep Copy
Definition:-
Shallow copy and deep copy are two concepts that describe how objects or data structures are cloned.
A shallow copy is a copy of an object that contains references to the original data. this copy points to the same memory location as the original. Changes made to the copy may affect the original data, and vice versa. Shallow copying is generally a faster and simpler process, since it only involves copying the references rather than the actual data.
A deep copy, on the other hand, is a copy of an object or data structure that creates a new copy of all the data. this copy is completely independent of the original, with its own memory locations. Changes made to the copy will not affect the original data, and vice versa. Deep copying is generally a slower and more complex process, since it involves copying all the data.
Now we have successfully implemented the prototype design pattern but one thing we should know while cloning we are using shallow copy or deep copy for creating the object. Cloneable Interface has a method clone which is working as a shallow copy.
Get Balkrishna Shukla’s stories in your inbox
Join Medium for free to get updates from this writer.
Now if you want deep copy in a use case then we have multiple ways to achieve this. One simple way to use SerializationUtils from apache commons.
import org.apache.commons.lang.SerializationUtils;
public abstract class Vehicle implements Cloneable {
/* as all the other things in this method is same as above code we are not writing
whole thing ..just implemeted method clone() */
@Override
public Object clone() throws CloneNotSupportedException {
return SerializationUtils.clone(this);
}
}Cache Implementation to improve performance
So we have implemented prototype design pattern above. Now you can use a cache to improve the performance of the Prototype pattern by storing prototype objects in memory and returning copies of them as needed. This can be especially useful if creating new prototype objects is expensive and time-consuming.
Now for caching you can use any type of caching (Redis , ehcache , Hashmap). We will use the java.util.HashMap class as a simple cache implementation. Here's an example implementation of the PrototypeFactory class that uses a HashMap to store pre-built vehicle prototypes.
public class PrototypeCacheFactory {
private static final HashMap<String, Vehicle> objectPrototypes = new HashMap<>();
static {
Car carPrototype = new Car("Toyota", "Corolla", 2023, "Red", 2);
objectPrototypes.put("Car", carPrototype);
Truck truckPrototype = new Truck("Ford", "F-150", 2019, "Blue", 2, 5000);
objectPrototypes.put("Truck", truckPrototype);
}
public static Vehicle getPrototype(String type) throws CloneNotSupportedException {
Vehicle prototype = objectPrototypes.get(type);
if (prototype != null) {
return (Vehicle) prototype.clone();
}
throw new IllegalArgumentException("Invalid vehicle type provided");
}
}In this example, the static block initialises the cache with the default prototypes for a car and a truck. The getPrototype() method takes a string argument that specifies the type of vehicle to create, and returns a copy of the corresponding prototype object from the cache.
We need to implement cache if necessary because it requires additional code to manage the pool of objects, and can increase memory usage if many objects are cached and not released properly.
Advantages :-
- You can clone objects without coupling to their concrete classes.
- You can avoid expensive or time taking object creation process.
- You can produce complex objects more conveniently.
Disadvantages:-
- cloning complex objects that have circular references can be tricky and requires careful handling to avoid infinite recursion or some other issues. Circular references occur when an object directly or indirectly refers to itself creating a cycle.
References :-
Head first design Patterns







