Bird
Raised Fist0

Prototype Pattern - Cloning Objects When Construction Is Expensive

LLDeasyInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Prototype Pattern - Cloning Objects When Construction Is Expensive
40 min ยท medium
medium

You are designing a system where creating certain objects is computationally expensive due to complex initialization (e.g., loading large data, network calls). To optimize performance, the system should clone existing prototype instances instead of constructing new ones from scratch. The system must support multiple prototype types, allow safe concurrent cloning, and enable adding new prototype types without modifying existing code.

  • Prototype objects have mutable internal state that must be deeply cloned to avoid shared references.
  • Cloning must be thread-safe to support concurrent requests without corrupting prototype state.
  • Prototype registry holds references to prototype instances and allows lookup by type.
  • New prototype types can be added without modifying existing prototype registry or client code.
  • Java's cloning mechanism (Cloneable interface) is known to be problematic; design should avoid its pitfalls.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Prototype Interface with Cloning Behavior
Design a Prototype interface that declares a cloning method. Consider how to express cloning behavior in Java without using the problematic Cloneable interface.
๐Ÿ’ก Hint
Use an abstract method clonePrototype() returning Prototype. Avoid Java's Cloneable interface due to shallow copy issues and checked CloneNotSupportedException.
Prototype interface with method Prototype clonePrototype(); interface chosen because it declares behavior without holding state; avoids Cloneable pitfalls.
Mid: Recognizes Prototype pattern with clone method. Senior: Explains why Cloneable interface is avoided due to shallow copying and checked exceptions in Java.
โš ๏ธCommon Wrong Answer
Implementing Cloneable and overriding clone() leads to shallow copies and forces handling CloneNotSupportedException, complicating client code.
Step 2
Implement Concrete Prototype Classes with Deep Cloning
Implement concrete prototype classes that hold mutable state and override clonePrototype() to perform deep cloning.
๐Ÿ’ก Hint
Use copy constructors or serialization-based cloning to achieve deep copy. Avoid default Object.clone() shallow copy.
ConcretePrototype classes implement clonePrototype() using copy constructors to perform deep cloning of mutable fields; avoids shallow copy issues of Object.clone().
Mid: Implements clonePrototype() method. Senior: Explains deep cloning necessity to prevent shared mutable state and how copy constructor ensures this.
โš ๏ธCommon Wrong Answer
Using Object.clone() default shallow copy causes shared mutable references leading to subtle bugs.
Step 3
Design Prototype Registry with Thread-Safe Access
Design a PrototypeRegistry class that stores prototype instances and allows clients to clone prototypes by type. Ensure thread-safe access to the registry.
๐Ÿ’ก Hint
Use ConcurrentHashMap<String, Prototype> for registry storage. Synchronize clone operations if prototype state is mutable during cloning.
PrototypeRegistry uses ConcurrentHashMap<String, Prototype> for thread-safe prototype lookup; clonePrototype() calls are synchronized on prototype instance to prevent race conditions during cloning.
Mid: Uses PrototypeRegistry to manage prototypes. Senior: Explains thread interleaving where concurrent clonePrototype() calls without synchronization can cause inconsistent state if cloning is not atomic.
โš ๏ธCommon Wrong Answer
Using HashMap without synchronization causes race conditions and visibility issues in concurrent access.
Step 4
Enable Extensibility Without Modifying Existing Code
Design the system so new prototype types can be added without modifying existing PrototypeRegistry or client code.
๐Ÿ’ก Hint
Use the Open/Closed Principle: registry accepts new prototypes via registration method; clients request clones by prototype type string or enum.
PrototypeRegistry exposes registerPrototype(String key, Prototype p) method; clients use keys to clone; new prototypes added by registering new keys without modifying registry code.
Mid: Recognizes Open/Closed Principle in design. Senior: Explains how registry design avoids modification of existing code, enabling extension via composition and polymorphism.
โš ๏ธCommon Wrong Answer
Hardcoding prototype types in registry violates Open/Closed Principle, requiring code changes for new types.
Step 5
Handle Prototype State Mutability and Concurrency
Explain how to handle mutable state inside prototypes during cloning to avoid concurrency bugs.
๐Ÿ’ก Hint
Synchronize clonePrototype() method or use immutable internal state snapshots during cloning.
clonePrototype() method declared synchronized in concrete prototypes to ensure atomic deep cloning; volatile not used because cloning is method-level atomic operation.
Mid: Uses synchronized clonePrototype(). Senior: Traces thread interleaving where Thread 1 partially clones mutable fields, Thread 2 reads inconsistent state without synchronization.
โš ๏ธCommon Wrong Answer
Not synchronizing clonePrototype() leads to partially cloned objects visible to other threads causing data corruption.
๐Ÿ”Œ
API Design Challenge
Design an API that allows clients to request clones of prototypes by type, ensuring thread-safe cloning and extensibility without modifying existing code.
  • Use a PrototypeRegistry class with thread-safe storage like ConcurrentHashMap<String, Prototype>.
  • Expose a registerPrototype(String key, Prototype p) method to add new prototype types dynamically.
  • Clients request clones by passing the prototype key string or enum to the registry's clone method.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Prototype interface with clonePrototype() method avoiding Cloneable interface
  • ConcretePrototype classes implementing deep cloning with copy constructors
  • PrototypeRegistry using ConcurrentHashMap for thread-safe prototype storage
  • Synchronized clonePrototype() method to ensure atomic cloning
  • PrototypeRegistry registerPrototype() method enabling Open/Closed Principle
Good to Have - 30%
  • PrototypeRegistry implemented as singleton with volatile instance and double-checked locking
  • Use of enums/constants for prototype keys to prevent string typos
  • Client code decoupled from concrete prototypes via Prototype interface
  • Explanation of thread interleaving issues without synchronization
Bonus - 10%
  • Serialization-based deep cloning as alternative to copy constructors
  • Use of immutable snapshots during cloning to avoid synchronization
โœ…
Model Answer
โ–ผ Reveal

    • Prototype interface declares Prototype clonePrototype() method without extending Cloneable to avoid checked exceptions and shallow copy.
    • ConcretePrototype classes implement clonePrototype() using copy constructors for deep cloning of mutable fields.
    • PrototypeRegistry uses ConcurrentHashMap<String, Prototype> for thread-safe prototype storage and lookup.
    • clonePrototype() method in concrete prototypes is synchronized to ensure atomic deep cloning under concurrent access.
    • PrototypeRegistry exposes registerPrototype(String key, Prototype p) method to add new prototypes without modifying existing code, enforcing Open/Closed Principle.
    • Avoid using Object.clone() due to shallow copy and CloneNotSupportedException checked exception.
    • PrototypeRegistry is a singleton with volatile instance and double-checked locking to ensure safe lazy initialization.
    • Use enums or constants for prototype keys to avoid runtime string typos and improve type safety.
    • Deep cloning ensures no shared mutable references between prototype and cloned instances, preventing subtle concurrency bugs.
    • Client code interacts only with Prototype interface and PrototypeRegistry, decoupling concrete prototype implementations.

    Practice

    (1/5)
    1. In a Java codebase, a factory class provides methods to create related objects like Button and Checkbox families without specifying their concrete classes. Which design pattern is applied here and why is it the correct choice?
    easy
    A. Singleton pattern, because it ensures only one factory instance exists.
    B. Builder pattern, because it constructs complex objects step-by-step.
    C. Abstract Factory pattern, because it creates families of related objects without specifying their concrete classes.
    D. Prototype pattern, because it clones existing objects to create new ones.

    Solution

    1. Step 1: Identify the role of the factory class

      The factory creates multiple related objects (Button, Checkbox) without exposing their concrete classes, indicating a family of products.
    2. Step 2: Match pattern intent

      Abstract Factory pattern is designed to create families of related objects without specifying their concrete classes, unlike Builder (stepwise construction), Singleton (single instance), or Prototype (cloning).
    3. Final Answer:

      Option C -> Option C
    4. Quick Check:

      Abstract Factory creates related object families without concrete classes [check]
    Hint: Abstract Factory creates related families, not single objects [check]
    Common Mistakes:
    2. In designing the traffic light system, should Vehicle be an abstract class or an interface, considering vehicles have state like speed and direction? Why?
    easy
    A. Vehicle should be an interface because it only defines behavior without state
    B. Vehicle should be an interface with default methods to hold state
    C. Vehicle should be an abstract class to encapsulate shared state and behavior
    D. Vehicle should be a final class to prevent subclassing and ensure immutability

    Solution

    1. Step 1: Analyze state vs behavior requirements

      Vehicles have shared state (speed, direction) and behavior; interfaces cannot hold instance state, so interface alone is insufficient.
    2. Step 2: Abstract class encapsulates shared state and common methods

      Abstract class allows shared fields and partial implementation, enabling subclasses to extend and customize behavior.
    3. Final Answer:

      Option C -> Option C
    4. Quick Check:

      Stateful base class -> abstract class, not interface [check]
    Hint: Stateful base class -> abstract class, stateless -> interface [check]
    Common Mistakes:
    3. In a multithreaded LRU Cache, if the put() method is not synchronized, which exact sequence of operations between Thread 1 and Thread 2 can cause the cache to contain duplicate keys or corrupt the doubly linked list?
    medium
    A. Thread 1 and Thread 2 both call get() simultaneously, causing stale reads of the cache order.
    B. Thread 1 updates the value of an existing key while Thread 2 evicts a different key, causing no race condition.
    C. Thread 1 removes the tail node while Thread 2 updates the head node, causing inconsistent pointers but no duplicate keys.
    D. Thread 1 checks if key exists -> false, context switch; Thread 2 checks if key exists -> false; both insert new nodes for the same key concurrently.

    Solution

    1. Step 1: Identify the race condition in put()

      Without synchronization, two threads can both check for key existence and find it missing before either inserts, leading to duplicate nodes for the same key.
    2. Step 2: Trace the exact interleaving

      Thread 1: check key -> false; context switch; Thread 2: check key -> false; both insert new nodes, corrupting the cache state.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Concurrent check-then-act without lock -> duplicate keys inserted [check]
    Hint: Check-then-act without lock causes duplicates [check]
    Common Mistakes:
    4. In a multi-threaded environment, if the addProductToCart() method is not synchronized, which exact sequence of events can cause the cart to contain duplicate entries for the same product?
    medium
    A. Thread 1 checks if product exists -> false, context switch; Thread 2 checks -> false; both add product concurrently.
    B. Thread 1 adds product, Thread 2 removes product simultaneously causing inconsistency.
    C. Thread 1 updates quantity, Thread 2 reads stale quantity due to lack of volatile keyword.
    D. Thread 1 locks cart, Thread 2 waits but eventually overwrites product quantity.

    Solution

    1. Step 1: Identify check-then-act race condition

      Both threads check if product exists and see false before either adds it, due to lack of synchronization.
    2. Step 2: Both threads add product concurrently causing duplicates

      Because the add operation is not atomic, both add the product, resulting in duplicates.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Check-then-act gap without synchronization -> duplicate entries [check]
    Hint: Check-then-act gap -> duplicate add without sync [check]
    Common Mistakes:
    5. The system needs to support ElectricCar and ChargingSpot without modifying existing classes. What is the minimum change to the current design to support this?
    hard
    A. Create a new ElectricCar class and ChargingSpot class, then modify Cart and Order to handle them explicitly.
    B. Modify Vehicle class to add isElectric flag and add logic for charging spots inside existing methods.
    C. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, update SpotCompatibility map; no existing class changes.
    D. Add a new interface Chargeable and implement it in ElectricCar and ChargingSpot, modifying existing classes to check for it.

    Solution

    1. Step 1: Identify extensibility without modifying existing classes

      Adding new enum values and new subclasses allows extension without changing existing class code.
    2. Step 2: Update SpotCompatibility map to include new types

      Updating compatibility map is a configuration change, not a class modification, preserving OCP.
    3. Final Answer:

      Option C -> Option C
    4. Quick Check:

      Enum extension + new subclasses + compatibility map update -> no existing class change [check]
    Hint: Extend enums + subclasses + config map -> no class edits [check]
    Common Mistakes:
    Recommended

    Ready to prove it?

    Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

    10 questions~5 mineasy hard