ReviseAlgo Logo

Creational Patterns

Prototype

Create new objects by cloning existing instances, bypassing constructor parameters and expensive initializations

Last Updated: June 26, 2026 23 min read

The Prototype Pattern is a creational design pattern that allows copying existing objects without making the code dependent on their concrete classes. It enables duplicating objects with complex, expensive-to-initialize structures, while preserving the encapsulation of private fields and references.

1. Learning Objectives

  • Identify when cloning existing objects is more efficient than instantiating via new.
  • Analyze the structural differences between a Shallow Copy and a Deep Copy.
  • Explain the limitations of Java's standard Cloneable interface and why copy constructors are preferred.
  • Synthesize a Prototype Registry to cache and reuse cloneable configurations.
  • Implement deep copy mechanisms in Java, Python, and C++.

2. Problem & Naive Solution

Suppose you are building a role-playing game (RPG). Instantiating a new GameCharacter (like a Warrior) requires loading texture graphics, reading base stat data from local database files, and initializing list containers (e.g., an inventory containing multiple weapons). This configuration process is computationally expensive.

The Naive Solution

To create a new, similar character (e.g., a clone of a pre-configured Warrior), a developer might instantiate a new object and manually copy the fields:

This approach has two major flaws:

  • Violates Encapsulation: The client must have direct visibility into the object's internal private fields (e.g. getters) to copy them, exposing implementation details.
  • Tight Coupling: The client must know the exact concrete class of the object (e.g. GameCharacter), making it impossible to copy objects polymorphically (e.g. cloning a generic Character interface reference).

3. Issues with Shared References

If the copy is performed naively by copying reference variables, it results in a Shallow Copy:

In this state, both characters share the same inventory list in memory. If Player 2 receives a new weapon, the weapon is added to Player 1's inventory as well, creating synchronization bugs and memory leaks.

4. Pattern Introduction & UML

The Prototype Pattern delegates the cloning process to the objects themselves. The base class or interface defines a standard method (usually clone()). The concrete subclass implements this method, creating a new instance and copying its own private fields (including deep copies of nested references) before returning the clone.

5. Participants

  • Prototype Interface (Prototype): Declares the interface containing the clone() method.
  • Concrete Prototype (GameCharacter): Implements the cloning interface, handling recursive deep copying.
  • Prototype Registry (CharacterRegistry): Caches a set of pre-configured prototype instances. When the client needs a new object, the registry returns a clone of the cached prototype.
  • Client: Requests the registry to clone a prototype instance, then customizes the clone's attributes.

6. Theory (Shallow vs. Deep Copy)

Shallow Copy

Copies primitive fields directly, but copies object references (e.g. arrays, lists, sub-objects) by value. Both the original object and the clone share references to the same sub-objects in memory.

Deep Copy

Recursively duplicates all nested objects. The clone receives its own independent allocations for all nested lists, maps, and sub-objects, ensuring complete isolation from the original object.

7. Syntax Explanation

Cloning semantics and syntax vary across languages:

  • Java: Java's standard Cloneable interface is widely criticized because it lacks a public clone() method declaration (it is a marker interface). Developers must override Object.clone(), cast the return type, and handle CloneNotSupportedException. Best practice is to use Copy Constructors (e.g. public GameCharacter(GameCharacter source)) to perform deep copying.
  • Python: Python provides a built-in copy module. copy.copy() performs a shallow copy, while copy.deepcopy() recursively duplicates all nested reference structures.
  • C++: C++ leverages copy constructors (e.g. GameCharacter(const GameCharacter& other)) called during assignment or passing parameters. Polymorphic cloning is achieved by declaring a virtual clone method returning a smart pointer (e.g. virtual std::unique_ptr<Prototype> clone() = 0;).

8. Step-by-Step Implementation

  1. Step 1: Create the Prototype interface containing the clone() method.
  2. Step 2: Implement the Prototype interface on the target class. Override the clone method using a copy constructor to clone the class fields.
  3. Step 3: Inside the copy constructor, perform deep copies for all nested mutable reference types (lists, maps, custom sub-objects) by instantiating new collections and copying elements.
  4. Step 4: Implement a Prototype Registry class containing a hash map to cache pre-configured prototype instances.
  5. Step 5: Modify client code to retrieve and clone prototype instances from the registry, bypassing constructor invocations.

9. Complete Code (Mini Project)

10. Code Walkthrough

Let's review the deep cloning logic:

  • In Java, the copy constructor public GameCharacter(GameCharacter source) is called by the clone() method. It instantiates a new ArrayList (copying the abilities strings) and calls the copy constructor of the nested Weapon object to duplicate it.
  • In C++, the copy constructor GameCharacter(const GameCharacter& other) initializes fields and calls std::make_unique<Weapon>(*other.weapon) to perform a deep copy of the unique pointer resource. This allocates a new Weapon on the heap, ensuring the clone has its own independent instance.
  • In Python, the copy.deepcopy() utility handles recursive cloning, duplicate reference checking, and circular reference tracking automatically.

11. Execution Flow

  1. Prototype Creation: The application instantiates and configures a base prototype character (Warrior), including loading external assets.
  2. Registration: The application registers this instance with the CharacterRegistry hash map.
  3. Cloning Request: The client calls registry.create("warrior_tier_1"). The registry retrieves the base Warrior instance and calls its clone() method.
  4. Deep Copying: The copy constructor runs, allocating new heap memory for the duplicate character, list containers, and nested weapon sub-objects.
  5. Customization: The client receives the cloned instance and customizes its properties (e.g., changing its name). The original prototype remains unmodified in the registry.

12. Internal Working (Memory Allocation)

The diagram below illustrates the heap layout differences between a shallow copy and a deep copy of a GameCharacter:

Shallow Copy (Shared Reference)

Original (0x1000) ──❯ Name: "Base"
                      Weapon: 0x5000 ──────┐
                                           │
                                           ▼
Clone    (0x2000) ──❯ Name: "Clone"        Weapon Object (0x5000)
                      Weapon: 0x5000 ──────┘ (Both share same instance!)
    

Deep Copy (Isolated Allocation)

Original (0x1000) ──❯ Name: "Base"
                      Weapon: 0x5000 ──❯ Weapon Object A (0x5000)

Clone    (0x2000) ──❯ Name: "Clone"
                      Weapon: 0x6000 ──❯ Weapon Object B (0x6000)
                                         (New independent instance!)
    

13. Complexity Analysis

  • Time Complexity: $O(R)$ where $R$ is the number of nested fields and references to clone. Deep copying requires visiting and duplicating every object in the hierarchy.
  • Space Complexity: $O(M)$ where $M$ is the memory size of the duplicated fields. Cloning allocates a complete new object hierarchy on the heap.

14. Best Practices

  • Prefer Copy Constructors over Java's Object.clone(): Copy constructors are type-safe, compile-time verified, and do not require handling checked exceptions.
  • Implement deep copying by default: When implementing the Prototype pattern, default to deep copying to prevent unexpected side effects from shared references.
  • Use a Prototype Registry: Cache pre-configured prototype instances in a registry to simplify retrieval and customization.

15. Common Mistakes

  • Performing a shallow copy on nested collections: Copying only the list reference instead of instantiating a new list, leading to shared state bugs.
  • Circular Reference Loops: If Object A references Object B, which references Object A, a naive recursive deep clone will enter an infinite loop. Use a map to track already-cloned objects (Python's deepcopy handles this automatically).
  • Cloning static fields: Duplicating class-level static fields during object cloning. Static fields belong to the class, not the instance, and should not be copied.

16. Framework Usage

  • Spring Bean Prototype Scope: In Spring, beans configured with scope="prototype" return a new instance every time they are requested from the container, acting as a dynamic object factory.
  • JavaScript Object.create(): JavaScript uses prototypical inheritance natively. Object.create(proto) creates a new object using the specified prototype object as its template.

17. Interview Discussion

Q: What is the main difference between a copy constructor and the clone() method in Java?
Answer: The clone() method relies on native JVM allocation, which bypasses class constructors and is not checked at compile time (returning a generic Object type). Copy constructors are standard, type-safe Java constructors, making them easier to debug, compile-time verified, and cleaner to write.
Q: How do you handle deep cloning in classes containing circular references?
Answer: By maintaining a hash map of already-cloned objects (e.g. Map<Object, Object> visited) during recursion. Before cloning an object, check if its address exists in the map. If so, return the already-cloned reference, breaking the infinite recursion loop.
Q: When should a developer prefer Prototype over Abstract Factory?
Answer: Use Abstract Factory when you want to create new objects from scratch using platform-specific classes. Use Prototype when instantiating new objects from scratch is expensive, or when you want to create new objects by copying and modifying the state of pre-configured template instances.

18. Practice Exercises

  • Easy: Write a cloneable ConfigTemplate class in Python that copies a dictionary configuration.
  • Medium: Implement a deep copy constructor for a BinaryTree node in Java, ensuring all child nodes are cloned recursively.
  • Hard: Write a C++ NetworkRouteRegistry that stores complex route layouts (vectors of pointers to connection nodes). Implement deep cloning for the routes, ensuring connection nodes are cloned without creating duplicate node instances in the clone path.

19. Challenge Problem

Design a Spreadsheet Cell Cloning Engine. A spreadsheet contains a grid of cells. Each cell has an identifier, a cell value, CSS styles (fonts, alignments), and a formula object that references other cells. When copying a range of cells, the engine must clone cells recursively. Write the implementation in Java, Python, or C++ and show how the clone process duplicates styles and formulas cleanly without causing circular reference lockups.

20. Summary & Cheat Sheet

  • The Prototype pattern duplicates existing objects (prototypes) to create new instances.
  • Shallow copies copy references, sharing sub-objects; deep copies duplicate the entire object hierarchy.
  • Prototype registries cache pre-configured prototypes to simplify retrieval and customization.
  • Copy constructors are generally preferred over Java's standard Cloneable interface.

21. Quiz

1. What is the primary purpose of the Prototype pattern?

A) To restrict instantiation of a class to a single object
B) To create new objects by cloning pre-configured existing instances (Correct)
C) To build objects step-by-step using method chaining

2. What is a key difference between a shallow copy and a deep copy?

A) Shallow copy duplicates nested objects; deep copy shares references
B) Shallow copy shares references to nested objects; deep copy recursively duplicates the entire hierarchy (Correct)
C) Shallow copy runs at compile time; deep copy runs at runtime

3. Why is Java's standard Cloneable interface widely criticized?

A) It does not support private fields
B) It does not declare a public clone() method, bypassing constructor safety checks (Correct)
C) It locks the class loaders

4. What role does a Prototype Registry play?

A) It registers JDBC database connection drivers
B) It compiles class files into binary modules
C) It caches and manages pre-configured prototype instances for cloning (Correct)

5. Which approach is preferred over Java's standard clone() method for copying objects?

A) Default constructors with setter methods
B) Copy Constructors (Correct)
C) Declaring all attributes as static

6. In Python, how is deep copying performed?

A) Using copy.copy()
B) Overriding the __new__ constructor method
C) Using copy.deepcopy() (Correct)

7. What is a common mistake when implementing the Prototype pattern?

A) Using prototype registries to manage clones
B) Performing a shallow copy on nested mutable collections, sharing references across instances (Correct)
C) Implementing clone methods on nested classes

8. How does prototypical inheritance work in JavaScript?

A) It compiles classes into native assembly code
B) Objects inherit properties directly from other prototype template objects (Correct)
C) It uses JVM classloaders to manage inheritance

9. In C++, why do clone methods return unique pointers (std::unique_ptr)?

A) To prevent compiler memory leaks during polymorphic cloning (Correct)
B) To enable method chaining
C) C++ clone methods can only return pointers

10. How can we prevent infinite recursion when cloning objects with circular references?

A) By declaring all class fields as final
B) By tracking already-cloned objects in a map during recursion (Correct)
C) By allocating objects on the stack instead of the heap

22. Next Lesson Preview

In the next module, we will explore Structural Design Patterns, starting with the Adapter Pattern, to learn how to connect incompatible interfaces together!