Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggyRazorpayPhonePe

Polymorphism - Compile-Time (Overloading) vs Runtime (Overriding)

Choose your preparation mode3 modes available
▶
Steps
setup

Define base class Shape

The base class Shape is defined with a draw() method that prints a generic message.

💡 This sets up the parent class that other shapes will inherit from, establishing the base behavior.
Line:class Shape: def draw(self): print("Drawing a generic shape")
💡 Shape class provides a default draw() method to be overridden by subclasses.
📊
Polymorphism - Compile-Time (Overloading) vs Runtime (Overriding) - Watch the Algorithm Execute, Step by Step
Watching each step reveals how polymorphism works internally, clarifying the difference between compile-time overloading and runtime overriding.
Step 1/10
·Active fill★Answer cell
Defines base class with a concrete method to be overridden.
Shape
+draw()
Subclass overrides base class method to customize behavior.
Shape
+draw()
Circle
+draw()
Circle ▷ Shape
Multiple subclasses override the same base method.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Objects instantiated from classes to demonstrate polymorphism.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Loop setup to traverse polymorphic objects.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Runtime method dispatch selects subclass method.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Runtime dispatch calls correct subclass method.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Base class method called when no override exists.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Iteration complete, polymorphic calls demonstrated.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape
Final understanding of runtime polymorphism via overriding.
Shape
+draw()
Circle
+draw()
Rectangle
+draw()
Circle ▷ ShapeRectangle ▷ Shape

Key Takeaways

✓ Runtime polymorphism allows method calls to invoke the correct subclass implementation dynamically.

This dynamic dispatch is not obvious from reading code alone but is clear when watching method calls resolve at runtime.

✓ Overriding replaces base class methods in subclasses, enabling flexible behavior customization.

Seeing the overridden methods highlighted helps understand how subclasses change behavior.

✓ The base class method is called only if no subclass override exists, showing fallback behavior.

This clarifies that polymorphism does not always mean subclass methods are called; base methods remain accessible.

Practice

(1/5)
1. When a class inherits from multiple classes that have a method with the same name, describe the step-by-step process the Method Resolution Order (MRO) uses to determine which method is called.
easy
A. MRO uses a linearization algorithm that merges the order of parents and their ancestors to find the method.
B. MRO searches the first parent class fully before moving to the next parent class.
C. MRO always calls the method from the last parent class listed in the inheritance.
D. MRO randomly picks the method from any parent class that defines it.

Solution

  1. Step 1: Understand naive search

    MRO does not simply search the first parent class fully before moving to the next; it uses a more sophisticated approach.
  2. Step 2: Recognize MRO linearization

    MRO uses a specific linearization (like C3 linearization) that merges parent classes and their ancestors in a consistent order.
  3. Step 3: Eliminate incorrect options

    MRO always calls the method from the last parent class listed in the inheritance is incorrect because the last parent is not always chosen; order and ancestors matter. MRO randomly picks the method from any parent class that defines it is incorrect because MRO is deterministic, not random.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    MRO merges inheritance hierarchies to find the correct method in a predictable order.
Hint: MRO = deterministic linearization of inheritance graph
Common Mistakes:
  • Assuming simple left-to-right search suffices
  • Believing last parent always overrides
  • Thinking method choice is random
2. Considering extensibility in the Snake and Ladder game design, what is a key trade-off when embedding snakes and ladders directly as attributes inside the Board class versus modeling them as separate entities?
medium
A. Modeling snakes and ladders as separate entities increases runtime complexity significantly
B. Embedding snakes and ladders inside Board simplifies design but reduces flexibility to add new types of board elements later
C. Embedding snakes and ladders inside Board improves encapsulation and makes the Board immutable
D. Modeling snakes and ladders separately forces duplication of position data, increasing memory usage unnecessarily

Solution

  1. Step 1: Embedding snakes/ladders inside Board

    This approach simplifies initial design but tightly couples Board to these elements.
  2. Step 2: Impact on extensibility

    Tightly coupled design makes it harder to add new board elements (e.g., portals, traps) without modifying Board.
  3. Step 3: Modeling as separate entities

    Allows easy extension by adding new element types without changing Board internals.
  4. Step 4: Complexity and memory considerations

    Separate entities add minimal overhead; runtime complexity impact is negligible.
  5. Final Answer:

    Option B -> Option B
  6. Quick Check:

    Trade-off is between simplicity and extensibility, not runtime complexity or memory bloat.
Hint: Tight coupling simplifies now but blocks future extensions [OK]
Common Mistakes:
  • Thinking separate entities cause big runtime overhead
  • Believing embedding improves encapsulation and immutability
  • Assuming separate entities cause memory bloat
3. Consider the following buggy code snippet from a thread-safe observer pattern implementation. Which line contains the subtle bug that can cause unsubscribed observers to still receive notifications?
def unsubscribe(self, observer, event_type):
    with self.lock:
        if event_type in self.observers:
            self.observers[event_type].remove(observer)
            if not self.observers[event_type]:
                del self.observers[event_type]
Assume that observer may not be subscribed to event_type when unsubscribe is called.
medium
A. Line 5: No handling of empty observer dictionary
B. Line 2: Lock is not acquired properly
C. Line 3: Missing check if observer is in the set before remove()
D. Line 4: Deleting event_type key prematurely

Solution

  1. Step 1: Analyze unsubscribe logic

    Line 3 calls remove() on the set without checking if observer exists, which raises KeyError if observer not subscribed.
  2. Step 2: Identify fix

    Check if observer in self.observers[event_type] before remove() to avoid errors and ensure proper removal.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    remove() without membership check causes runtime error [OK]
Hint: remove() without membership check can raise error [OK]
Common Mistakes:
  • Assuming remove() silently ignores missing elements
4. Suppose you want to extend the Command Pattern to support commands that can be executed multiple times (reused) and also support undo/redo correctly. Which modification to the invoker's undo/redo management is necessary to handle this scenario without corrupting state?
hard
A. Store only one instance of each command and reuse it for all executions, pushing it multiple times onto undo stack.
B. Clear both undo and redo stacks after every command execution to avoid reuse issues.
C. Create a new command instance for each execution and push that instance onto the undo stack, ensuring undo/redo operate on the correct state snapshot.
D. Avoid using undo/redo stacks and instead recompute state from scratch on each undo or redo.

Solution

  1. Step 1: Understand reuse implications

    Reusing the same command instance for multiple executions causes undo/redo to affect the wrong state because the command's internal state may change.
  2. Step 2: Identify correct approach

    Creating a new command instance per execution ensures each undo/redo corresponds to the exact executed command instance and its parameters.
  3. Step 3: Confirm undo/redo stack integrity

    This approach preserves correct ordering and state consistency during undo/redo operations.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    New instances per execution prevent state corruption on undo/redo [OK]
Hint: Each execution needs a fresh command instance for correct undo/redo [OK]
Common Mistakes:
  • Reusing command instances causing incorrect undos
  • Clearing stacks unnecessarily losing history
5. If the Library Management System must support reserving books that are currently loaned out, which design change best handles the edge case where multiple users reserve the same book simultaneously?
hard
A. Let the User class maintain their own reservation list independently without central coordination.
B. Allow multiple reservations by updating the book's status to 'reserved' without tracking order.
C. Add a reservation queue in the LoanManager and process reservations in FIFO order with atomic updates.
D. Implement a polling mechanism where users repeatedly check book availability without reservations.

Solution

  1. Step 1: Why a reservation queue?

    FIFO queue ensures fairness and order in handling multiple reservations.
  2. Step 2: Why not multiple reservations without order?

    Allow multiple reservations by updating the book's status to 'reserved' without tracking order. causes ambiguity and race conditions in fulfilling reservations.
  3. Step 3: Why not user-maintained reservations?

    Let the User class maintain their own reservation list independently without central coordination. lacks centralized control, risking inconsistent reservation states.
  4. Step 4: Why not polling without reservations?

    Implement a polling mechanism where users repeatedly check book availability without reservations. leads to poor user experience and race conditions.
  5. Final Answer:

    Option C -> Option C
  6. Quick Check:

    Centralized, ordered reservation management with atomic updates handles concurrency and fairness.
Hint: Use a centralized FIFO reservation queue to handle concurrent reservations fairly [OK]
Common Mistakes:
  • Ignoring reservation order and fairness
  • Decentralizing reservation tracking
  • Relying on user polling instead of system-managed reservations
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