Prototype – A Creational Design Pattern

Creational Design Pattern – Initial Concept

This springcavaj – Creational Design Pattern page briefly describes the Initial Concept of Creational Design Patterns, advantages, disadvantages, benefits, various types, common problems, and a few interview questions discussed.

This guide will provide a deep understanding of Creational Design Patterns for designing and architecting an application.

What is a Prototype Design Pattern?

The Prototype Pattern is a Creational Design Pattern used to create duplicate objects while keeping the performance overhead low. It allows you to copy an existing object instead of creating a new one from scratch. This pattern is particularly useful when object creation is costly or complex. In the Prototype Pattern, the object to be copied must implement a clone method. This is typically achieved by implementing the Cloneable interface in Java.

What are the features of the Prototype Design Pattern?

  • Object Cloning – Enables object duplication at runtime.
  • Customization – Allows creating objects with slight variations by modifying the cloned object.
  • Runtime Object Creation – Avoids creating new instances using constructors repeatedly.
  • Reduces Initialization Cost – Particularly, useful when initializing an object involves significant resources or computation.

What are the advantages of Prototype Design Patterns?

  • Improved Performance – Cloning objects is often faster than creating new instances, especially for resource-intensive objects.
  • Dynamic Object Creation – Objects can be created dynamically at runtime without binding them to a specific class.
  • Decoupling – The client code is decoupled from the class instantiation logic.
  • Prevents Duplication of Logic – Reuses already initialized objects with minimal overhead.

What are the disadvantages of Prototype Design Patterns?

  • Complex Cloning – Cloning can become complicated if the object has complex object graphs or references to other objects.
  • Deep vs. Shallow Copy – Deep copying of objects requires extra care, as shallow copies may not suffice for all use cases.
  • Cloneable Interface Limitation – In Java, the Cloneable interface has limitations and does not provide a default implementation for clone().

What are the use cases of Prototype Design Patterns?

  • Game Development – Cloning game objects like enemies, projectiles, or terrain.
  • UI Components – Creating UI elements with similar properties and behaviors.
  • Database Entities – Cloning entities for batch processing or caching.
  • Object Caching – Maintaining a registry of frequently used objects and cloning them when needed.

Brief description of implementation

I have developed a small program to help you understand the Builder Design Pattern’s Working Principle. The code is uploaded to GitHub as springcavaj-designpattern repository. Please clone the application and import in any of the IDs like either STS or Eclipse.

After successfully importing the above code base, one will find a class named PrototypeMasterclassApplication.java in the package as com.springcavaj.designpattern.prototype. Please see the screenshot attached below.

Here you can see the class under the com.springcavaj.designpattern.prototype package is PrototypeMasterclassApplication.java. Right-click on the class, select the Run As option, and then Java Application. It will run and generate the output as provided below.

INFO com.springcavaj.designpattern.prototype.PrototypeMasterclassApplication - Original Spring Prototype Object : SpringPrototypeImpl [dependencyInjection=DI, inversionOfControl=IOC] and hash code is : 2047329716

INFO com.springcavaj.designpattern.prototype.PrototypeMasterclassApplication - Clone Spring Prototype Object : SpringPrototypeImpl [dependencyInjection=DI, inversionOfControl=IOC], and hash code is : 96639997

Herein, in the above output, one can notice I have printed the hash value of both the original and clone objects. And both the values are different to each other which clearly proves that both the objects are different.

Detailed Explanation

In this small program, I have defined one interface named SpringPrototype.java where I have defined only 1 method named clone() and it returns the instance of SpringPrototype. An implementation class named SpringPrototypeImpl which implements the interface named SpringPrototype and overrides the clone(). This SpringPrototypeImpl.java class defines 2 straight forward properties as dependencyOfInjection and inversionOfControl both are of String data types. It defines the concept of Construction injection, where these properties are set through the constructor and only getters are defined for these 2 properties in the class. It also overrides the clone() method which creates a new instance of SpringPrototypeImpl objects. The code snippet of SpringPrototypeImpl.java is provided below.

public class SpringPrototypeImpl implements SpringPrototype {
	
	private String dependencyInjection;
	private String inversionOfControl;
	
	public SpringPrototypeImpl(String dependencyInjection, String inversionOfControl) {
		super();
		this.dependencyInjection = dependencyInjection;
		this.inversionOfControl = inversionOfControl;
	}

	public String getDependencyInjection() {
		return dependencyInjection;
	}

	public String getInversionOfControl() {
		return inversionOfControl;
	}

	@Override
	public SpringPrototype clone() {
		return new SpringPrototypeImpl(this.dependencyInjection, this.inversionOfControl);
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("SpringPrototypeImpl [dependencyInjection=");
		builder.append(dependencyInjection);
		builder.append(", inversionOfControl=");
		builder.append(inversionOfControl);
		builder.append("]");
		return builder.toString();
	}
	
}

In the PrototypeMasterclassApplication.java I have created one original object and another clone object using the clone() as implemented by SpringPrototypeImpl.java class. In the logs, I have printed the hash value of both objects, and it prints different values for both objects. It proves that the clone() method is creating a new object of SpringPrototypeImpl.

public class PrototypeMasterclassApplication {

	private static Logger logger = LoggerFactory.getLogger(PrototypeMasterclassApplication.class);
	
	public static void main(String[] args) {
		SpringPrototypeImpl prototypeImplOriginal = new SpringPrototypeImpl("DI", "IOC");
		
		SpringPrototypeImpl prototypeImplClone = (SpringPrototypeImpl) prototypeImplOriginal.clone();
		
		logger.info("Original Spring Prototype Object : {} and hash code is : {}", prototypeImplOriginal, prototypeImplOriginal.hashCode());
		logger.info("Clone Spring Prototype Object : {}, and hash code is : {}", prototypeImplClone, prototypeImplClone.hashCode());
	}

}

Deep Understanding of Shallow and Deep Cloning

Shallow Cloning and Deep Cloning are equivalent if the underlying class doesn’t consist of any other referenced objects. If the class consists of referenced objects, then we need the concept of shallow and deep cloning. In case of referenced objects, shallow cloning means it will hold the dame copy of reference objects whereas in deep cloning it will hold the separate copy of all the referenced objects.

Shallow Cloning

  • The cloned object gets a copy of the original object’s fields.
  • If the object contains references to other objects, only the references are copied, not the actual objects.
  • Changes made to the referenced objects in the clone will reflect in the original object (and vice versa).

Deep Cloning

  • Both the object and all referenced objects are copied.
  • Changes to the referenced objects in the clone do not affect the original object and vice versa.
  • This is done by recursively cloning the referenced objects.

Java Code Example of both Shallow and Deep Cloning

Reference Object CodeDependencyInjection.java

public class DependencyInjection implements Cloneable {
	
	private String dependencyOfInjection;

	public DependencyInjection(String dependencyOfInjection) {
		super();
		this.dependencyOfInjection = dependencyOfInjection;
	}

	public String getDependencyOfInjection() {
		return dependencyOfInjection;
	}

	public void setDependencyOfInjection(String dependencyOfInjection) {
		this.dependencyOfInjection = dependencyOfInjection;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("DependencyInjection [dependencyOfInjection=");
		builder.append(dependencyOfInjection);
		builder.append("]");
		return builder.toString();
	}
	
	@Override
	protected DependencyInjection clone() throws CloneNotSupportedException {
		return (DependencyInjection) super.clone();
	}

}

Actual CodeSpring.java

public class Spring implements Cloneable {
	
	private DependencyInjection dependencyInjection;
	private List<String> inversionOfControls;
	
	public Spring(DependencyInjection dependencyInjection, List<String> inversionOfControls) {
		super();
		this.dependencyInjection = dependencyInjection;
		this.inversionOfControls = inversionOfControls;
	}

	public DependencyInjection getDependencyInjection() {
		return dependencyInjection;
	}

	public List<String> getInversionOfControls() {
		return inversionOfControls;
	}
	
	@Override
	public Spring clone() throws CloneNotSupportedException {
		return (Spring) super.clone();
	}
	
	public Spring deepClone() throws CloneNotSupportedException {
		DependencyInjection dependencyInjection = this.dependencyInjection.clone();
		List<String> inversionOfControls = new ArrayList<>();
		return new Spring(dependencyInjection, inversionOfControls);
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("Spring [dependencyInjection=");
		builder.append(dependencyInjection);
		builder.append(", inversionOfControls=");
		builder.append(inversionOfControls);
		builder.append("]");
		return builder.toString();
	}
	
}

Main ApplicationShallowDeepCloningMasterclassApplication.java

public class ShallowDeepCloningMasterclassApplication {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(ShallowDeepCloningMasterclassApplication.class);

	public static void main(String[] args) throws CloneNotSupportedException {
		DependencyInjection dependencyInjection = new DependencyInjection("DI - Clone");
		List<String> inversionOfControls = new ArrayList<>();
		inversionOfControls.add("IOC - Original");
		Spring springOriginal = new Spring(dependencyInjection, inversionOfControls);
		
		// Shallow Clone
		Spring springShallowCopy = springOriginal.clone();
		
		// Deep Clone
		Spring springDeepCopy = springOriginal.deepClone();
		
		springOriginal.getDependencyInjection().setDependencyOfInjection("DI - Original");
		springOriginal.getInversionOfControls().add("IOC - Clone");
		
		LOGGER.info("Original object : {}", springOriginal);
		LOGGER.info("Shallow Clone object : {}", springShallowCopy);
		LOGGER.info("Deep Clone object : {}", springDeepCopy);
 	}

}

Analysis

  • Shallow Cloning
    • The springShallowCopy object shares the same dependencyInjection and inversionOfControls as the springOriginal object.
    • Changes to the springOriginal affects the springShallowCopy.
  • Deep Cloning
    • The springDeepCopy object has its own copy of dependencyInjection and inversionOfControls.
    • Changes to the springOriginal‘s properties will not affect the springDeepCopy‘s properties.

Impact of Shallow Cloning and Deep Cloning

TopicsShallow CloningDeep Cloning
PerformanceIt is faster because it copies only references for nested objects.It is slower because it recursively clones all nested objects.
Memory UsageIt uses less memory since it does not duplicate nested objects.It uses more memory because it duplicates all referenced objects.
Use CasesIt is suitable for immutable or simple objects where nested objects don’t change.It is essential for mutable objects or when changes in one object must not affect the other.
ComplexityIt is straightforward to implement.It requires custom logic to ensure all nested objects are properly cloned.

Common Problems Faced

  • In shallow clone, changes to the nested object in the clone affect the original. So, use deep cloning to create separate copies of nested objects.
  • Deep Cloning can be tedious for objects with complex hierarchies. In this case, use the Apache libraries for serialization/deserialization for deep cloning.
  • Performance overhead in deep cloning for large objects. Optimize by selectively deep cloning only the necessary fields.

Steps to run the application

The complete Run application steps are provided in this README.md file, but still, I am providing the steps to run points below.

  • Clone the sprincavaj-designpattern application from the GitHub repository.
  • Import the application as a Maven application, either in STS or Eclipse.
  • Find the PrototypeMasterclassApplication.java class.
  • Right-click on the file and select Run As -> Java Application.

GitHub Code Link

Download the Source Code from GitHub

Common Problems Faced Implementing Prototype Pattern

Prototype Design Pattern Common Problems

Interview FAQs

Prototype Design Pattern Interview FAQs

Other Useful Links

Implementations of various components of Spring Boot

Other Design Pattern Links

Factory Design Pattern

Abstract Factory Design Pattern

Builder Design Pattern

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *