Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleFlipkartMicrosoft

Prototype Pattern - Deep Copy vs Shallow Copy

Choose your preparation mode3 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Define Profile class with fields and methods

The Profile class is defined with two fields: name (string) and scores (list). The __deepcopy__ method is declared but not yet executed.

💡 Understanding the class structure is essential because the copy behavior depends on how fields are handled in the clone method.
Line:class Profile: def __init__(self, name, scores): self.name = name self.scores = scores
💡 The Profile class holds nested mutable data (scores list), which is critical for demonstrating shallow vs deep copy.
📊
Prototype Pattern - Deep Copy vs Shallow Copy - Watch the Algorithm Execute, Step by Step
Watching each step of copying and cloning reveals the difference between shallow and deep copy, which is often confusing when only reading code.
Step 1/12
·Active fillAnswer cell
Class encapsulates data and cloning behavior for prototype pattern
Profile
+name: string
+scores: list[int]
+__init__()
+__deepcopy__()
Object instantiation from class
Profile
+name: string
+scores: list[int]
+__init__()
+__deepcopy__()
original
+name: string
+scores: list[int]
original Profile (1:1)
Method activation during cloning
Profile
+name: string
+scores: list[int]
+__init__()
+__deepcopy__()
original
+name: string
+scores: list[int]
original Profile (1:1)
Deep copy handles immutable fields efficiently
Profile
+name: string
+scores: list[int]
+__deepcopy__()
Recursive deep copy of nested mutable objects
Profile
+name: string
+scores: list[int]
+__deepcopy__()
scores
+0: int
+1: int
Profile scores (1:2)
New object instantiation with deep copied data
Profile
+name: string
+scores: list[int]
+__deepcopy__()
copy_obj
+name: string
+scores: list[int]
copy_obj Profile (1:1)
Reading object state
original
+name: string
+scores: list[int]
Reading copy object state
copy_obj
+name: string
+scores: list[int]
Modifying nested data in copy
copy_obj
+name: string
+scores: list[int]
Verifying original object state after copy modification
original
+name: string
+scores: list[int]
Verifying copy object state after modification
copy_obj
+name: string
+scores: list[int]
Conceptual difference between shallow and deep copy
Profile
+name: string
+scores: list[int]
original
+scores: list[int]
shallow_copy
+scores: list[int]
original Profile (1:1)shallow_copy Profile (1:1)original shallow_copy (1:1)

Key Takeaways

Deep copy creates fully independent nested objects, preventing side effects when the copy is modified.

This is hard to see from code alone because nested references look similar; watching the copy of each nested object clarifies this.

Immutable fields like strings do not require new memory allocation during deep copy, optimizing performance.

Understanding this helps learners see why deep copy is selective and efficient.

Shallow copy shares nested references, so modifying the copy affects the original, which can cause bugs.

Seeing the conceptual difference visually helps learners grasp why deep copy overrides are necessary.

Practice

(1/5)
1. When a vehicle arrives at the parking lot entrance, trace the sequence of interactions among components to allocate a parking spot and update the system state.
easy
A. Vehicle requests spot allocation from ParkingLot, which uses AllocationStrategy to find a spot, then ParkingSpot is marked occupied
B. ParkingSpot directly checks if it can fit the vehicle and marks itself occupied without consulting ParkingLot
C. Vehicle marks a ParkingSpot as occupied and informs ParkingLot afterward
D. ParkingLot assigns a spot randomly without checking vehicle type or spot availability

Solution

  1. Step 1: Identify correct flow

    The Vehicle initiates the request but does not allocate itself. ParkingLot coordinates allocation using a strategy component to find a suitable spot.
  2. Step 2: Update state

    Once a spot is found, ParkingSpot is marked occupied, and ParkingLot updates its records.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Centralized coordination and proper state updates ensure consistency [OK]
Hint: Allocation is coordinated by ParkingLot using strategy, not by Vehicle or ParkingSpot alone [OK]
Common Mistakes:
  • Assuming ParkingSpot can allocate itself
  • Vehicle directly marking spots occupied
  • Random assignment ignoring constraints
2. Trace the sequence of events when a client requests a service via Dependency Injection (DI) in an IoC container. Which step correctly follows the previous?
easy
A. Client creates the service instance directly, then passes it to the IoC container.
B. Client requests the service from the IoC container, which then creates and injects dependencies into the client.
C. Service creates the client instance and injects itself into the client.
D. IoC container instantiates the service and injects it into the client before the client uses it.

Solution

  1. Step 1: Understand DI and IoC flow

    In Dependency Injection via IoC, the client requests a service from the container, which manages creation and injection.
  2. Step 2: Analyze options

    Client requests the service from the IoC container, which then creates and injects dependencies into the client. correctly describes the client requesting the service and the container creating and injecting dependencies. Client creates the service instance directly, then passes it to the IoC container. reverses roles incorrectly. IoC container instantiates the service and injects it into the client before the client uses it. suggests container injects before client requests, which is inaccurate. Service creates the client instance and injects itself into the client. incorrectly states the service creates the client.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    IoC container controls creation; client depends on container to provide dependencies.
Hint: In DI, client asks container; container creates and injects dependencies.
Common Mistakes:
  • Thinking client creates service instances directly
  • Assuming container injects dependencies before client requests
  • Confusing who controls object creation
3. What is the time complexity of constructing a complex object using the Builder pattern when the object has k parts to build?
medium
A. O(k), because each of the k parts is built sequentially
B. O(1), since each build method is called once
C. O(k^2), due to nested calls between builder methods
D. O(log k), as parts are built using divide-and-conquer

Solution

  1. Step 1: Identify number of build steps

    The Builder pattern calls a build method for each part, so k parts mean k method calls.
  2. Step 2: Analyze time per build call

    Each build method adds a part in O(1) time, so total time is O(k).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Linear time proportional to number of parts built [OK]
Hint: Each part built once -> O(k) time [OK]
Common Mistakes:
  • Confusing constant time with O(k)
  • Assuming nested calls cause O(k^2)
4. Which of the following is a common trade-off or limitation when strictly applying the Open/Closed Principle in a large software system?
medium
A. It forces all changes to be made in a single base class, increasing risk of bugs
B. It can lead to excessive class proliferation, making the codebase harder to navigate
C. It eliminates the need for interfaces or abstract classes, simplifying design
D. It guarantees zero runtime overhead due to polymorphism

Solution

  1. Step 1: Identify trade-offs of OCP

    Strict adherence often results in many small subclasses, increasing complexity.
  2. Step 2: Why other options are false

    It forces all changes to be made in a single base class, increasing risk of bugs is opposite to OCP's goal; changes are made via extension, not base modification. It eliminates the need for interfaces or abstract classes, simplifying design is false because OCP relies on abstractions like interfaces. It guarantees zero runtime overhead due to polymorphism is incorrect; polymorphism can introduce slight runtime overhead.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Class explosion is a known practical downside of OCP.
Hint: OCP can cause many small classes [OK]
Common Mistakes:
  • Thinking OCP centralizes changes in base classes
  • Believing OCP removes need for abstractions
  • Assuming polymorphism has no runtime cost
5. Suppose you want to extend the payment system to allow switching payment strategies at runtime based on user input, including invalid or unsupported methods. Which modification best supports this requirement while maintaining clean design?
hard
A. Use a factory method to get the strategy instance and inject it into PaymentProcessor; handle invalid methods by raising exceptions.
B. Keep the strategy selection logic inside the PaymentProcessor's pay method with if-else chains.
C. Hardcode all payment methods inside PaymentProcessor and add a default fallback strategy for invalid inputs.
D. Remove the strategy interface and implement all payment methods inside PaymentProcessor with switch-case.

Solution

  1. Step 1: Understand runtime strategy switching

    Switching strategies at runtime requires decoupling strategy selection from the context and handling invalid inputs gracefully.
  2. Step 2: Identify design that supports clean extensibility and error handling

    Using a factory method to create strategy instances and injecting them into PaymentProcessor allows runtime flexibility and clean error handling via exceptions.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Factory + DI + exceptions enable runtime switching and robustness [OK]
Hint: Factory and DI enable runtime strategy switching with error handling [OK]
Common Mistakes:
  • Hardcoding strategies or using conditionals inside context