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.
💡 The template method defines the fixed sequence of steps, enforcing the algorithm's structure.
setup
Setup: Define Subclass Tea with Overrides
The Tea subclass overrides the abstract methods brew and add_condiments, and also overrides the hook method customer_wants_condiments to disable condiments.
💡 This step shows how subclasses customize specific steps and optionally disable parts of the algorithm via hooks.
💡 Subclass overrides complete the algorithm customization by providing specific condiments.
final_state
Final State: Template Method Pattern Execution Complete
Both Tea and Coffee have executed their prepare_recipe methods. Tea skipped condiments due to hook override; Coffee included condiments. The algorithm skeleton was preserved by the base class.
💡 This final step summarizes how the template method pattern enforces algorithm structure while allowing subclass customization.
💡 The pattern separates invariant algorithm steps from variant ones, promoting reuse and flexibility.
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 fill★Answer 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.
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
Step 1: Dice roll triggers GameController
Dice generates number; GameController uses it to calculate tentative position.
Step 2: Board checks for ladder or snake
GameController queries Board to see if new position has ladder or snake.
Step 3: GameController updates Player position
Based on Board's info, GameController updates Player's position accordingly.
Step 4: Player position is finalized
Player's position reflects ladder climb if applicable.
Final Answer:
Option A -> Option A
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
Step 1: Inspect build_pool method
The build_pool method contains only 'pass', so it does not add the 'Pool' part to the house.
The Director calls build_pool expecting the pool to be added, but due to missing implementation, it is not.
Final Answer:
Option D -> Option D
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
Step 1: Understand the Diamond Problem
Diamond Problem arises when a class inherits from two classes that share a common ancestor, causing ambiguity.
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.
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.
Step 4: Evaluate Multiple inheritance reduces code reuse compared to single inheritance.
Multiple inheritance generally increases code reuse by combining features from multiple classes.
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.
Final Answer:
Option D -> Option D
Quick Check:
Complexity and maintainability are key trade-offs in multiple inheritance.
Hint: Multiple inheritance = power with complexity cost
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
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.
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.
Final Answer:
Option B -> Option B
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
Step 1: Understand the requirement
Observers can subscribe multiple times to the same event type and should receive multiple notifications accordingly.
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.
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.
Final Answer:
Option A -> Option A
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