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
Use cloning to efficiently create object copies when construction is costly
easyDesign Patterns - AppliedFreq: medium
Prerequisites:OOP BasicsJava Interfaces and Abstract ClassesJava Cloning (Cloneable interface and Object.clone())
⚙️
Functional Requirements
  • Provide a common interface or abstract class for cloning
  • Allow registering and retrieving prototype instances by key
  • Clone method must return a deep copy to prevent shared mutable state
  • Ensure cloning is thread-safe when accessed concurrently
  • Avoid modifying existing prototype instances when cloning
  • Support extensibility to add new prototype types without modifying existing code
  • Handle CloneNotSupportedException properly
👤
Use Cases
  • Client requests a new object instance without invoking expensive constructor
  • PrototypeManager returns a cloned copy of a registered prototype
  • Client modifies cloned object independently of prototype
  • PrototypeManager supports registering new prototype instances
  • Handle cloning failures gracefully
  • Support deep cloning for objects with mutable fields
🧑‍💻
Actors
  • Client
  • PrototypeManager
  • ConcretePrototype
❓
Clarifying Questions to Ask
  • How many concurrent threads will request cloning? Is thread safety required?
  • Should the cloning be shallow or deep? Are there mutable nested objects?
  • What is the expected frequency of registering new prototypes?
  • How to handle cloning failures or unsupported clone operations?
  • Is there a pricing or resource cost associated with cloning or construction?
  • Are prototype instances mutable after cloning?
  • Should the PrototypeManager be a singleton or multiple instances allowed?
Out of Scope
✗ Serialization-based cloning
✗ Prototype pattern usage for immutable objects
✗ Caching or pooling of cloned objects
✗ Distributed cloning across network
🔢
Enumerations
enum CloneType
SHALLOW | DEEP
Defines cloning depth explicitly to avoid ambiguity and enforce correct clone implementation. Enables exhaustive switch and compile-time safety.
enum PrototypeStatus
REGISTERED | CLONED | FAILED
Tracks lifecycle state of prototype instances to manage cloning success and error handling.
📦
Prototype
2 fields
protected String idprotected PrototypeStatus status
Defines the cloning contract and holds common prototype state. Abstract class to allow state and default clone implementation.
Why thisAbstract class allows sharing state and default clone implementation, unlike interfaces which cannot hold state.
Why notInterfaces lack state and default method implementations; using abstract class simplifies cloning logic.
public abstract Prototype clonePrototype() throws CloneNotSupportedExceptionAbstract method to be implemented by concrete prototypes to return a cloned copy.
public String getId()Returns the unique identifier of the prototype.
📦
ConcretePrototypeA
2 fields
private String expensiveResourceprivate int configValue
Implements deep cloning logic for a prototype with expensive construction and mutable fields.
Why thisProvides custom deep clone to avoid expensive reinitialization of resources.
Why notAvoids costly constructor calls by cloning existing initialized instance.
public ConcretePrototypeA clonePrototype() throws CloneNotSupportedExceptionPerforms deep clone by copying mutable fields and creating new instances as needed.
private void initializeExpensiveResource()Simulates expensive construction logic.
📦
ConcretePrototypeB
1 fields
private List<String> items
Implements cloning for prototype containing mutable collection requiring deep copy.
Why thisEnsures cloned object has independent mutable collection to prevent shared state.
Why notShallow copy would share collection reference causing side effects.
public ConcretePrototypeB clonePrototype() throws CloneNotSupportedExceptionCreates deep copy of items list to avoid shared mutable state.
📦
PrototypeManager
1 fields
private final Map<String, Prototype> prototypes
Manages registration and retrieval of prototype instances. Provides thread-safe cloning access.
Why thisCentralizes prototype management and cloning logic with synchronization.
Why notAvoids direct client cloning and inconsistent prototype state.
public synchronized void registerPrototype(String key, Prototype prototype)Registers a prototype instance with a unique key.
public synchronized Prototype getClone(String key) throws CloneNotSupportedExceptionReturns a cloned copy of the prototype identified by key.
📦
Client
1 fields
private PrototypeManager prototypeManager
Requests cloned objects from PrototypeManager and uses them independently.
Why thisDecouples client from prototype creation details and expensive construction.
Why notAvoids direct instantiation and costly object creation.
public Prototype createObjectFromPrototype(String key) throws CloneNotSupportedExceptionObtains a cloned object from PrototypeManager.
🔗
Relationships
ConcretePrototypeA──►Prototype[1]
inheritanceinherits lifecycle from Prototype
ConcretePrototypeB──►Prototype[1]
inheritanceinherits lifecycle from Prototype
PrototypeManager◇──►Prototype[0..*]
aggregationPrototype instances can exist independently of PrototypeManager
Client──►PrototypeManager[1]
associationClient depends on PrototypeManager but does not own it
🗄️
prototypes
PK: id
id STRINGstatus STRINGdata BLOB
Stores serialized prototype objects with their status.
🗄️
prototype_registry
PK: key
key STRINGprototype_id STRINGregistration_time TIMESTAMP
Registry mapping keys to prototype IDs with registration timestamps.
🔌
POST
Register Prototype
Request
{
  "key": "string",
  "prototypeData": "serialized object"
}
Response
{
  "status": "success or failure",
  "message": "string"
}
Registers a new prototype instance with a unique key.
🔌
GET
Get Cloned Prototype
Request
{
  "key": "string"
}
Response
{
  "clonedPrototypeData": "serialized object"
}
Returns a deep cloned copy of the prototype identified by key.
🔌
GET
List Registered Prototypes
Request
{}
Response
{
  "keys": [
    "string"
  ]
}
Lists all registered prototype keys.

Practice

(1/5)
1. In the Library Management System, the ReservationManager class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct use case?
easy
A. Prototype pattern, to clone ReservationManager instances efficiently.
B. Factory pattern, because it controls object creation centrally.
C. Builder pattern, to construct ReservationManager with complex parameters.
D. Singleton pattern, to ensure only one ReservationManager instance manages all reservations.

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single instance is created and accessed globally.
  2. Step 2: Why Singleton fits ReservationManager

    ReservationManager must coordinate all reservations centrally to avoid conflicts; multiple instances would cause inconsistent state.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In a Workflow Engine design, a Step class has a private constructor and a static synchronized getInstance() method that returns the single instance. Which design pattern is applied here and why is it the correct choice?
easy
A. Singleton pattern, because it ensures only one instance of Step exists to maintain consistent workflow state.
B. Prototype pattern, because it clones existing Step instances to avoid costly creation.
C. Builder pattern, because it separates construction from representation of Step objects.
D. Factory Method pattern, because it controls object creation through a static method.

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance, matching the Singleton pattern.
  2. Step 2: Why Singleton fits Workflow Engine Step

    Singleton ensures consistent state across the workflow engine by having only one Step instance managing transitions, avoiding conflicting states.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static synchronized method -> Singleton [check]
Common Mistakes:
3. In a multi-threaded parking system using the Decorator pattern, the parkVehicle() method checks if a spot is available and then marks it occupied. Without synchronization, which exact thread interleaving causes double-booking?
medium
A. Thread 1 checks spot available -> true, context switch; Thread 2 checks spot available -> true; both mark spot occupied concurrently.
B. Thread 1 marks spot occupied, Thread 2 waits and then parks safely.
C. Thread 1 parks vehicle, Thread 2 reads spot status after parking completes.
D. Thread 1 and Thread 2 both wait on a lock, preventing double-booking.

Solution

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

    Without synchronization, Thread 1 reads spot as available (true) but before marking it occupied, context switches.
  2. Step 2: Trace Thread 2's actions

    Thread 2 also reads spot as available (true) and proceeds to mark it occupied. Both threads think the spot is free and park vehicles, causing double-booking.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act without sync allows both threads to park simultaneously [check]
Hint: Check-then-act gap causes double-booking without sync [check]
Common Mistakes:
4. In the Ride Booking system, the parkVehicle() method checks if a parking spot is available and then marks it occupied. Without synchronization, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 checks isAvailable() -> true, context switch before marking occupied; Thread 2 checks isAvailable() -> true, both mark occupied.
B. Thread 1 reads spot as occupied, Thread 2 reads spot as free, both park simultaneously.
C. Thread 1 marks spot occupied, Thread 2 waits and then parks after spot is freed.
D. Thread 1 and Thread 2 both wait on a lock, preventing double-booking.

Solution

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

    Without synchronization, Thread 1 reads spot as free (isAvailable() -> true) but is preempted before marking it occupied.
  2. Step 2: Thread 2 also reads spot as free and proceeds to mark it occupied, causing double-booking.

    This is the classic check-then-act gap causing race conditions.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without sync -> double-booking race [check]
Common Mistakes:
5. You need to add support for ElectricCar and ChargingSpot in your adapter-based parking system. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar extending Vehicle, and update SpotCompatibility.COMPATIBILITY_MAP; no existing classes modified.
B. Modify Vehicle class to add electric-specific fields and methods, and update ParkingLot to handle charging spots.
C. Create a new adapter class ElectricCarAdapter that modifies existing Vehicle and Spot classes to support charging.
D. Add a flag isElectric to Vehicle and add charging logic inside existing adapter classes.

Solution

  1. Step 1: Identify extensibility requirements

    New vehicle and spot types must be added without modifying existing classes to respect OCP.
  2. Step 2: Evaluate Java-specific implementation

    Adding new enum constants and new classes extends functionality cleanly; updating compatibility map centralizes logic without class changes.
  3. Step 3: Reject options modifying existing classes

    Options B, C, and D modify existing classes or add flags, violating OCP and risking class explosion.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    New enums and classes added, existing classes unchanged [check]
Hint: Add enums and classes, avoid modifying existing ones [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