Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartTCSInfosys

Inheritance - Types, Method Resolution Order & Diamond Problem

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 Base Class A

We define the base class A with a method 'method' that prints 'A'. This is the root of the diamond.

💡 Starting with the base class is essential to understand how derived classes inherit and override methods.
Line:class A: def method(self): print('A')
💡 Base class methods form the foundation for inheritance and method resolution.
📊
Inheritance - Types, Method Resolution Order & Diamond Problem - Watch the Algorithm Execute, Step by Step
Watching the step-by-step construction and method lookup in the diamond inheritance pattern reveals the subtlety of MRO and why it matters to avoid ambiguity.
Step 1/10
·Active fillAnswer cell
Defines base class with a concrete method.
A
+method()
Subclass B overrides method from A.
A
+method()
B
+method()
B A (1:1)
Subclass C overrides method from A.
A
+method()
B
+method()
C
+method()
B A (1:1)C A (1:1)
Multiple inheritance creates diamond shape.
A
+method()
B
+method()
C
+method()
D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
MRO computed using C3 linearization to resolve diamond problem.
A
+method()
B
+method()
C
+method()
D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
Instance created and method call initiated.
A
+method()
B
+method()
C
+method()
D
instance: D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
Method lookup moves to B after D lacks method.
A
+method()
B
+method()
C
+method()
D
instance: D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
Method resolved to B's implementation.
A
+method()
B
+method()
C
+method()
D
instance: D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
Method execution confirms MRO correctness.
A
+method()
B
+method()
C
+method()
D
instance: D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)
MRO resolves diamond inheritance ambiguity.
A
+method()
B
+method()
C
+method()
D
B A (1:1)C A (1:1)D B (1:1)D C (1:1)

Key Takeaways

Method Resolution Order (MRO) linearizes multiple inheritance hierarchies to avoid ambiguity.

Reading code alone rarely clarifies how MRO works internally; visualization shows the exact search order.

The diamond inheritance problem arises when two subclasses inherit from the same base and a subclass inherits both, causing potential method conflicts.

Seeing the diamond shape and method lookup path visually helps understand why this is a problem.

Python's C3 linearization algorithm ensures a consistent and predictable method lookup order in complex inheritance graphs.

The step-by-step MRO computation and method call resolution demonstrate this algorithm concretely.

Practice

(1/5)
1. In designing a parking lot system using OOP, which component is best suited to decide which type of parking spot (e.g., compact, large, handicapped) should be allocated to an incoming vehicle?
easy
A. The ParkingLot class, as it manages all spots and vehicles
B. The ParkingSpot class, since it represents the spot's characteristics
C. The Vehicle class, because it knows its own size and type
D. A Factory or Strategy pattern component that encapsulates the allocation logic

Solution

  1. Step 1: Understand responsibilities

    The Vehicle class only knows about itself, not allocation rules. The ParkingSpot class represents a spot but doesn't decide allocation. The ParkingLot manages overall state but delegating allocation logic to a dedicated component improves modularity.
  2. Step 2: Recognize design pattern role

    The Factory or Strategy pattern encapsulates allocation logic, allowing easy extension and modification without changing core classes.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Allocation logic centralized -> easier to maintain and extend [OK]
Hint: Allocation logic belongs in a dedicated pattern component, not core entities [OK]
Common Mistakes:
  • Assigning allocation responsibility to Vehicle or ParkingSpot classes
  • Putting all logic inside ParkingLot class leading to tight coupling
2. 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
3. Which of the following statements about using inheritance to model different vehicle types in the parking lot system is INCORRECT?
medium
A. Inheritance allows sharing common vehicle attributes and behaviors in a base Vehicle class
B. Using inheritance for vehicle types can lead to rigid designs that are hard to extend with new types
C. Inheritance is always the best approach to add new vehicle types without modifying existing code
D. Polymorphism enables treating all vehicle types uniformly when allocating parking spots

Solution

  1. Step 1: Review inheritance benefits

    Inheritance supports code reuse and polymorphism for uniform handling.
  2. Step 2: Recognize limitations

    Inheritance hierarchies can become rigid and hard to extend, violating open-closed principle.
  3. Step 3: Identify incorrect statement

    Claiming inheritance is always best ignores alternatives like composition or interfaces that improve extensibility.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Inheritance is not always best for extensibility [OK]
Hint: Inheritance can cause rigidity; prefer composition for extensibility [OK]
Common Mistakes:
  • Assuming inheritance is always the best design choice
  • Ignoring polymorphism benefits
4. 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
5. Suppose you want to extend the Template Method Pattern to allow clients to optionally skip multiple steps dynamically at runtime (not just condiments). Which modification best preserves the pattern's structure and flexibility?
hard
A. Override the entire template method in each subclass to conditionally skip steps as needed.
B. Add multiple hook methods in the base class for each optional step, with default implementations returning True or False.
C. Remove the base class and implement each beverage's recipe independently with duplicated code.
D. Use a flag parameter in prepare_recipe to decide which steps to execute, breaking encapsulation.

Solution

  1. Step 1: Understand requirement

    Need to optionally skip multiple steps dynamically while preserving fixed sequence and reuse.
  2. Step 2: Evaluate options

    Adding multiple hook methods in base class allows subclasses to override selectively without breaking skeleton.
  3. Step 3: Reject other options

    Overriding entire template method duplicates code and breaks pattern; flags break encapsulation; removing base class loses reuse.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Multiple hooks preserve flexibility and structure [OK]
Hint: Use hooks for optional steps, not override template method [OK]
Common Mistakes:
  • Overriding template method to skip steps
  • Using flags breaking encapsulation