Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggy

Template Method Pattern - Define Skeleton, Override Steps

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

Setup: Define Base Class CaffeineBeverage

The abstract base class CaffeineBeverage is defined with the template method 'prepare_recipe' that calls steps boil_water, brew, pour_in_cup, and conditionally add_condiments. Abstract methods brew and add_condiments are declared.

💡 This step establishes the algorithm skeleton that subclasses will follow and override. Understanding this structure is key to grasping the pattern.
Line:class CaffeineBeverage(ABC): def prepare_recipe(self): self.boil_water() self.brew()
💡 The template method defines the fixed sequence of steps, enforcing the algorithm's structure.
📊
Template Method Pattern - Define Skeleton, Override Steps - Watch the Algorithm Execute, Step by Step
Watching each step of the template method execution reveals how inheritance and method overriding work together to enforce a consistent algorithm structure while allowing flexible customization.
Step 1/14
·Active fillAnswer cell
Defines the template method pattern skeleton with abstract and concrete steps.
«abstract»CaffeineBeverage
+prepare_recipe()
+boil_water()
+brew()
+3 more
Subclass overrides abstract steps and hook to customize behavior.
Tea
+brew()
+add_condiments()
+customer_wants_condiments()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Subclass overrides abstract steps but uses default hook to include condiments.
Coffee
+brew()
+add_condiments()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Template method calls concrete step boil_water from base class.
Tea
+prepare_recipe()
+boil_water()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Subclass method brew overrides abstract step.
Tea
+brew()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Base class concrete method pour_in_cup reused.
Tea
+pour_in_cup()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Hook method controls optional step execution.
Tea
+customer_wants_condiments()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Optional step pruned by hook decision.
Tea
+add_condiments()
«abstract»CaffeineBeverage
Tea CaffeineBeverage
Template method calls concrete step boil_water from base class.
Coffee
+prepare_recipe()
+boil_water()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Subclass method brew overrides abstract step.
Coffee
+brew()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Base class concrete method pour_in_cup reused.
Coffee
+pour_in_cup()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Hook method controls optional step execution.
Coffee
+customer_wants_condiments()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Subclass method add_condiments overrides abstract step.
Coffee
+add_condiments()
«abstract»CaffeineBeverage
Coffee CaffeineBeverage
Template Method pattern enforces algorithm skeleton with subclass customization via overrides and hooks.
«abstract»CaffeineBeverage
+prepare_recipe()
+boil_water()
+brew()
+3 more
Tea
+brew()
+add_condiments()
+customer_wants_condiments()
Coffee
+brew()
+add_condiments()
Tea CaffeineBeverageCoffee CaffeineBeverage

Key Takeaways

The template method defines a fixed algorithm skeleton, ensuring consistent execution order.

This insight is hard to see from code alone because the flow is split across base and subclasses; visualization shows the enforced sequence clearly.

Subclasses override abstract steps to customize parts of the algorithm without changing its structure.

Seeing the method calls and overrides step-by-step reveals how polymorphism enables flexible behavior.

Hook methods allow subclasses to enable or disable optional steps dynamically, pruning the algorithm flow.

The conditional execution controlled by hooks is subtle in code but becomes obvious when watching the decision and pruning steps.

Practice

(1/5)
1. Trace the sequence of events when a player rolls the dice and lands on a ladder in a well-designed Snake and Ladder game. Which of the following best describes the correct order of internal state changes?
easy
A. Player rolls dice -> GameController calculates new position -> Board checks for ladder -> GameController updates Player position accordingly
B. Player rolls dice -> Player position updated -> Board checks for ladder -> Player position updated again if ladder found
C. Player rolls dice -> Board updates Player position directly if ladder found -> Player notified of new position
D. Player rolls dice -> Dice notifies Board -> Board updates Player position -> GameController confirms move

Solution

  1. Step 1: Dice roll triggers GameController

    Dice generates number; GameController uses it to calculate tentative position.
  2. Step 2: Board checks for ladder or snake

    GameController queries Board to see if new position has ladder or snake.
  3. Step 3: GameController updates Player position

    Based on Board's info, GameController updates Player's position accordingly.
  4. Step 4: Player position is finalized

    Player's position reflects ladder climb if applicable.
  5. Final Answer:

    Option A -> Option A
  6. Quick Check:

    Only GameController manages state changes; Board is queried but does not update Player directly.
Hint: GameController mediates all state changes after dice roll [OK]
Common Mistakes:
  • Assuming Player updates position before Board check
  • Believing Board directly updates Player position
  • Thinking Dice notifies Board
2. Identify the bug in the following Builder pattern code snippet that constructs a luxury house:
medium
A. get_result method returns the wrong object
B. Constructor does not initialize the House object
C. Director does not call build_roof method
D. Line with 'pass' in build_pool method lacks adding 'Pool' part

Solution

  1. Step 1: Inspect build_pool method

    The build_pool method contains only 'pass', so it does not add the 'Pool' part to the house.
  2. Step 2: Check Director.construct_luxury_house calls

    The Director calls build_pool expecting the pool to be added, but due to missing implementation, it is not.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Missing pool addition causes incomplete luxury house [OK]
Hint: Empty build_pool method -> missing part added [OK]
Common Mistakes:
  • Confusing constructor initialization
  • Ignoring missing method implementation
3. What is a key trade-off or limitation when using multiple inheritance to solve the Diamond Problem in object-oriented design?
medium
A. Multiple inheritance always leads to ambiguous method calls that cannot be resolved.
B. Multiple inheritance eliminates the need for Method Resolution Order (MRO).
C. Multiple inheritance reduces code reuse compared to single inheritance.
D. Using multiple inheritance can increase complexity and make the class hierarchy harder to understand and maintain.

Solution

  1. Step 1: Understand the Diamond Problem

    Diamond Problem arises when a class inherits from two classes that share a common ancestor, causing ambiguity.
  2. Step 2: Evaluate Multiple inheritance always leads to ambiguous method calls that cannot be resolved.

    Multiple inheritance can cause ambiguity, but languages use MRO to resolve it, so it is not always unresolved.
  3. Step 3: Evaluate Multiple inheritance eliminates the need for Method Resolution Order (MRO).

    MRO is essential in multiple inheritance to resolve method calls, so multiple inheritance does not eliminate MRO.
  4. Step 4: Evaluate Multiple inheritance reduces code reuse compared to single inheritance.

    Multiple inheritance generally increases code reuse by combining features from multiple classes.
  5. Step 5: Correct trade-off

    Using multiple inheritance can increase complexity and make the class hierarchy harder to understand and maintain. correctly identifies that multiple inheritance increases complexity and can make hierarchies harder to maintain.
  6. Final Answer:

    Option D -> Option D
  7. Quick Check:

    Complexity and maintainability are key trade-offs in multiple inheritance.
Hint: Multiple inheritance = power with complexity cost
Common Mistakes:
  • Believing multiple inheritance always causes irresolvable ambiguity
  • Thinking MRO is unnecessary with multiple inheritance
  • Assuming multiple inheritance reduces code reuse
4. What is the time complexity of notifying observers in the thread-safe observer pattern implementation where observers are stored in a dictionary mapping event types to sets of observers, and a snapshot list is created during notification?
medium
A. O(n) where n is total number of all observers across all event types
B. O(k) where k is the number of observers subscribed to the notified event type
C. O(k + n) where k is observers for event type and n is total observers
D. O(1) constant time due to hash set usage

Solution

  1. Step 1: Identify the data structure and notification process

    Observers are stored per event type in sets. Notification locks and copies only the observers for the specific event type.
  2. Step 2: Analyze complexity of notify()

    Notify creates a snapshot list of observers for the event type (size k) and iterates over it, so time is proportional to k, not total n.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Notify cost depends only on observers for that event type [OK]
Hint: Notify complexity depends on observers for event type only [OK]
Common Mistakes:
  • Assuming notify iterates over all observers regardless of event type
5. Suppose you want to extend the observer pattern to allow observers to receive multiple notifications for the same event type without unsubscribing (i.e., observers can be registered multiple times for the same event). Which modification to the thread-safe observer pattern implementation below correctly supports this requirement without breaking thread safety or notification correctness? Options: A) Change the observers data structure from a set to a list per event type and allow duplicates. B) Keep using a set but add a counter for each observer to track multiple subscriptions. C) Use a dictionary mapping observers to their subscription counts per event type. D) Use a queue per event type to enqueue notifications and process them asynchronously.
hard
A. Keep using a set but add a counter for each observer to track multiple subscriptions.
B. Change the observers data structure from a set to a list per event type and allow duplicates.
C. Use a dictionary mapping observers to their subscription counts per event type.
D. Use a queue per event type to enqueue notifications and process them asynchronously.

Solution

  1. Step 1: Understand the requirement

    Observers can subscribe multiple times to the same event type and should receive multiple notifications accordingly.
  2. Step 2: Evaluate data structure changes

    Using a set alone disallows duplicates. A list allows duplicates but is not thread-safe and inefficient for removals. A dictionary mapping observers to counts tracks multiple subscriptions safely.
  3. Step 3: Choose the best approach

    Adding a counter per observer in the set (Keep using a set but add a counter for each observer to track multiple subscriptions.) or using a dictionary (Use a dictionary mapping observers to their subscription counts per event type.) can work, but adding a counter per observer in the set is simpler and keeps thread safety with minimal changes.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Counting subscriptions per observer preserves multiple notifications safely [OK]
Hint: Track subscription counts to allow multiple notifications [OK]
Common Mistakes:
  • Using list causes concurrency issues and inefficient removals