Skip to main content
Create your own
Lesson illustration

Prototype Pattern: Cloning for Object Creation

Hello! Welcome to our fifth lesson on Creational Design Patterns.

In the last lesson, we focused on the Builder pattern, a powerful technique for constructing complex objects step-by-step, especially when you need to create an immutable object with many optional parameters. The Builder constructs a new object from scratch.

Today, we'll explore a different approach to object creation: the Prototype pattern. Your learning goal is to apply the Prototype pattern for object creation via cloning, distinguishing shallow vs. deep copy. Instead of building an object from the ground up, this pattern allows you to create new objects by copying an existing, pre-configured instance. This is particularly useful when object creation is an expensive operation.

The Problem: Expensive Object Creation

Imagine you have an object that requires significant work to instantiate. This cost could come from:

  • Heavy computations during initialization.
  • Loading large configuration files from disk.
  • Making network calls or database queries to fetch initial state.

If you need to create many similar objects, repeating this expensive initialization process every time with new MyObject() can severely impact your application's performance. The Prototype pattern offers a much more efficient alternative.

Let's start by exploring the fundamental challenges of copying an object in an object-oriented language and see how the Prototype pattern provides a robust solution.

Prototype | LLD

This article from AlgoMaster clearly explains the core problem the Prototype pattern solves. Creating objects isn't always as simple as new MyObject(). The resource details the challenges of object copying and introduces the Prototype pattern as an elegant solution.

Please read the sections 'The Challenge of Cloning Objects' and 'The Problem: Spawning Enemies in a Game'. Focus on why a naive approach to copying an object fails due to encapsulation and class dependencies, and how letting the object clone itself solves this.

As you've just read, the essence of the Prototype pattern is to shift the responsibility of creating a copy from an external client to the object itself. An object that knows how to clone itself can provide a copy without exposing its private fields or forcing the client to know its concrete class.

The Core of the Prototype Pattern: Shallow vs. Deep Copy

Now that you understand why we need the Prototype pattern, let's examine how to implement it. The pattern's structure is simple, but its implementation hinges on one absolutely critical concept: the difference between a shallow copy and a deep copy. Your ability to distinguish between these two is crucial for both applying the pattern correctly and for success in LLD interviews.

  • Shallow Copy: Creates a new object and copies the values of the fields from the original object. If a field is a primitive type (like int, boolean), its value is copied. If a field is a reference to another object (like a List or a custom Address object), only the reference is copied. This means both the original and the cloned object will point to the exact same nested object in memory.

  • Deep Copy: Creates a new object and, like a shallow copy, copies primitive values. However, for fields that are references to other objects, it recursively creates copies of those objects as well. The result is a completely independent clone; the original and the copy share no objects.

Prototype Design Pattern In Java - Examples Using Java 21+

This article from JavaTechOnline explains the pattern's structure and provides a detailed breakdown of the shallow vs. deep copy distinction. This is the most important concept in this lesson.

First, review the 'Structure (UML & Components)' section to get a high-level view. Then, read the 'Shallow vs Deep Clone' section very carefully. Understand when a shallow copy is sufficient and why a deep copy is necessary for objects with mutable fields.

To visualize this key difference, take a look at the following diagram.

Shallow Copy vs Deep Copy - UML Diagram and Memory Representation
This diagram illustrates the memory layout for shallow and deep copies. In a shallow copy, both the original and the clone share the same reference to the nested `List` object. In a deep copy, the nested `List` is also cloned, making the original and the clone completely independent.

If you perform a shallow copy and then modify a mutable nested object through the clone, you will inadvertently change the state of the original object as well. This side effect is a common source of bugs. A deep copy prevents this entirely.

Test your understanding!

You have a User class with a String name and an Address address object. The Address class has String street and String city fields and is mutable (it has setters).

If you implement a clone() method for the User class that only copies the name and the reference to the address object (a shallow copy), what happens if you clone a user and then change the city of the cloned user's address?

Show answer

Because it's a shallow copy, both the original User object and the cloned User object will share the same Address object in memory. Therefore, changing the city on the cloned user's address will also change the city for the original user. This is a common bug and a key reason why deep copying is often necessary.

To fix this, the User's clone() method must also create a new, independent Address instance for the cloned User, for instance by calling this.address.clone() if the Address class also supports cloning.

Implementation in Java

In Java, there are a few ways to implement cloning:

  1. Cloneable interface and Object.clone(): This is the traditional Java way. You implement the Cloneable marker interface and override the protected Object clone() method. However, this approach has several well-known issues: Object.clone() performs a shallow copy by default, it's cumbersome to use, and many in the Java community consider it a flawed design. While you should know it exists for interviews, it's often avoided in modern code.

  2. Copy Constructors: A safer and more explicit approach is to define a constructor that takes an instance of the same class as an argument and copies its state.

    public class Document {
        private String title;
        // other fields...
    
        // Copy constructor
        public Document(Document other) {
            this.title = other.title;
            // copy other fields...
        }
    }
    
  3. Custom clone() or copy() method: You can define your own public clone() method (independent of Cloneable) that uses a copy constructor or manual field-by-field copying. This is clear, explicit, and gives you full control.

Let's see how this works in a practical example that requires a deep copy.

Prototype Design Pattern In Java - Examples Using Java 21+

Let's solidify your understanding with a complete code example. The following section demonstrates how to implement the Prototype pattern for a Document object that contains a list of nested Section objects, explicitly showing how to perform a deep copy using a custom clone method.

Please study the 'Concrete Examples' section, focusing on the 'Document with nested content' example. Pay close attention to the clone() method in the Document class and see how it iterates through the list of sections to create new Section objects, ensuring a deep copy.

The example you just reviewed highlights the most important takeaway: when an object contains other mutable objects, its clone() method must delegate the cloning process to those contained objects to achieve a true deep copy.

Enhancement: The Prototype Registry

A common and useful extension of the Prototype pattern is the Prototype Registry (also called a Prototype Manager). This is essentially a cache or factory that stores a collection of pre-configured prototype objects, usually in a Map.

Instead of holding a direct reference to a prototype, the client code can request a clone from the registry using a key (e.g., a String name). This decouples the client even further, as new prototypes can be added to the registry at runtime without any changes to the client code.

Prototype Design Pattern In Java - Examples Using Java 21+

The 'Prototype Registry' acts as a centralized cache of prototype objects. This decouples the client even further, as the client only needs to know a key (like a string name) to request a clone.

Skim the 'Prototype Registry / Prototype Factory Variant' section to understand how a Map can be used to store and retrieve prototypes. This is a very practical technique.

Conclusion

You have now added the Prototype pattern to your toolkit. It provides an elegant and performant way to create new objects when the cost of construction is high.

Key Takeaways:

  • Purpose: To create new objects by copying an existing instance (a "prototype"), avoiding the cost of creation from scratch.
  • Core Implementation: An object implements a clone() method that returns a copy of itself.
  • Shallow vs. Deep Copy: This is the most critical concept. A shallow copy shares references to nested objects, while a deep copy creates a fully independent object graph. You must use deep copy if you want to modify the clone without affecting the original.
  • Modern Java Practice: Custom clone() methods or copy constructors are generally preferred over the built-in Cloneable interface for their clarity and safety.
  • Prototype Registry: A centralized manager that provides clones on demand based on a key, further decoupling clients from concrete prototype implementations.

In our next and final lesson on creational patterns, we will compare and contrast all the patterns we've learned: Singleton, Factory Method, Abstract Factory, Builder, and Prototype. We will analyze their trade-offs and develop a framework for choosing the most appropriate pattern for a given design problem, a skill that is highly valued in system design interviews.

Can't find a good explanation? Sign up and we'll make it for you

Sign up