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 fill★Answer 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
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.
Step 2: Update state
Once a spot is found, ParkingSpot is marked occupied, and ParkingLot updates its records.
Final Answer:
Option A -> Option A
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
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.
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.
Final Answer:
Option B -> Option B
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
Step 1: Identify number of build steps
The Builder pattern calls a build method for each part, so k parts mean k method calls.
Step 2: Analyze time per build call
Each build method adds a part in O(1) time, so total time is O(k).
Final Answer:
Option A -> Option A
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
Step 1: Identify trade-offs of OCP
Strict adherence often results in many small subclasses, increasing complexity.
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.
Final Answer:
Option B -> Option B
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
Step 1: Understand runtime strategy switching
Switching strategies at runtime requires decoupling strategy selection from the context and handling invalid inputs gracefully.
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.
Final Answer:
Option A -> Option A
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