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
Initialize Receiver and Invoker
Create the Receiver object which holds the state and the Invoker which manages command execution and undo/redo stacks.
💡 Setting up these objects is essential because the Receiver performs actions, and the Invoker controls command execution and history.
Line:receiver = Receiver()
invoker = Invoker()
💡 The Receiver and Invoker are distinct roles: Receiver knows how to perform actions; Invoker manages command lifecycle.
insert
Create CommandA and Execute
Create CommandA (AddCommand with value 1) linked to the Receiver and execute it via Invoker. CommandA is pushed onto the undo stack; redo stack is cleared.
💡 Executing a command changes the receiver's state and records the command for potential undo.
💡 Commands can be stored for deferred execution, enabling flexible command management.
execute
Execute Command Queue
Invoker executes all commands in the queue in order: CommandC then CommandD. Each command is executed, pushed onto undo stack, and redo stack is cleared after each execution.
💡 Executing queued commands applies their effects and updates undo/redo stacks as if executed individually.
Line:invoker.execute_queue()
💡 Batch execution of queued commands integrates seamlessly with undo/redo mechanism.
reconstruct
Final State: Receiver and Stacks
All commands have been executed or undone/redone as per input. The receiver's state reflects all executed commands. Undo stack contains all executed commands. Redo stack is empty. Command queue is empty.
💡 The final state shows the cumulative effect of all operations and the current undo/redo history.
Line:# No code executed, final state snapshot
💡 The undo and redo stacks accurately track command history, enabling flexible state rollback and reapplication.
Command Pattern - Undo/Redo, Request Queuing & Logging - Watch the Algorithm Execute, Step by Step
Watching the algorithm execute step-by-step reveals how commands are managed, how undo and redo stacks interact, and how queued commands are processed, which is difficult to grasp from code alone.
Step 1/8
·Active fill★Answer cell
Separation of concerns: Receiver handles state changes; Invoker manages command execution and history stacks.
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
Command encapsulation and execution with history tracking.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Sequential command execution with undo stack growth.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Undo operation moves command from undo to redo stack and reverts receiver state.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Redo operation moves command from redo to undo stack and reapplies receiver state.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Command queue stores commands for batch execution later.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Batch execution integrates queued commands into undo/redo stacks.
AddCommand
−receiver: Receiver
−value: int
+execute()
+undo()
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Final state shows command pattern managing state and history stacks correctly.
Receiver
−state: int
+action()
+revert()
Invoker
−undo_stack: List<Command>
−redo_stack: List<Command>
−command_queue: List<Command>
+execute_command()
+undo()
+redo()
+2 more
AddCommand → Receiver (1:1)
Key Takeaways
✓ Commands encapsulate all information needed to perform and undo an action, enabling flexible execution and history management.
This insight is hard to see from code alone because the separation of command logic from execution context is subtle without visualization.
✓ Undo and redo stacks maintain a clear history of executed and undone commands, allowing precise state rollback and reapplication.
Visualizing stack changes clarifies how commands move between undo and redo, which is often confusing when reading code.
✓ Queuing commands allows batching multiple operations for later execution, integrating seamlessly with undo/redo mechanisms.
Seeing queued commands and their execution order helps understand how deferred execution fits into the pattern.
Practice
(1/5)
1. You need to create different types of vehicles (cars, bikes) that share a common interface, and sometimes you want to create entire families of related vehicles (e.g., electric car + electric bike) ensuring consistency. Which design pattern best fits this requirement?
easy
A. Builder Pattern, because it constructs complex objects step-by-step.
B. Abstract Factory Pattern, because it creates families of related objects ensuring consistency.
C. Factory Pattern, because it creates simple objects based on a type parameter.
D. Singleton Pattern, because it ensures only one instance of each vehicle type.
Solution
Step 1: Understand the requirement for families of related objects
The problem states the need to create related vehicles (e.g., electric car and electric bike) that belong to a family and must be consistent.
Step 2: Match pattern to requirement
Abstract Factory is designed to create families of related objects, ensuring that the created objects are compatible and consistent.
Final Answer:
Option B -> Option B
Quick Check:
Abstract Factory creates related object families, Factory creates single objects [OK]
Hint: Families of related objects -> Abstract Factory [OK]
Common Mistakes:
Confusing Factory with Abstract Factory
Using Builder for simple object creation
2. In which scenario is applying the Liskov Substitution Principle (LSP) most critical to ensure system correctness?
easy
A. When a subclass adds new methods without overriding any superclass methods.
B. When a subclass uses composition instead of inheritance.
C. When a subclass narrows the input parameter types of an overridden method.
D. When a subclass extends a superclass but changes the expected behavior of inherited methods.
Solution
Step 1: Understand LSP's core requirement
LSP requires that subclasses can replace their superclasses without altering desirable properties of the program, especially behavior.
Step 2: Analyze each option carefully
When a subclass extends a superclass but changes the expected behavior of inherited methods. describes a subclass changing expected behavior, which violates LSP. When a subclass adds new methods without overriding any superclass methods. is safe as adding methods doesn't break substitutability. When a subclass narrows the input parameter types of an overridden method. narrows input types (contravariance violation), which breaks substitutability but is less direct than changing behavior. When a subclass uses composition instead of inheritance. is unrelated to LSP since composition is an alternative to inheritance.
Final Answer:
Option D -> Option D
Quick Check:
Only changing inherited method behavior breaks LSP directly.
Hint: LSP is about preserving inherited behavior, not just adding features.
Common Mistakes:
Thinking adding methods breaks LSP
Confusing covariance and contravariance in parameters
Assuming composition relates directly to LSP
3. You need to design a system where a fixed sequence of steps is followed to prepare different types of beverages, but some steps vary depending on the beverage type. Which design approach best ensures code reuse and enforces the sequence while allowing subclasses to customize specific steps?
easy
A. Implement separate classes with duplicated code for each beverage type.
B. Apply a greedy algorithm to select steps dynamically at runtime based on beverage type.
C. Use a base class defining the skeleton of the algorithm with abstract methods for variable steps, overridden by subclasses.
D. Use a brute force approach that tries all possible step sequences and picks the correct one.
Solution
Step 1: Identify the problem structure
The problem requires a fixed sequence of steps with some steps varying by subclass.
Step 2: Match to design pattern
The Template Method Pattern defines a skeleton algorithm in a base class and lets subclasses override specific steps, ensuring reuse and consistent sequence.
Final Answer:
Option C -> Option C
Quick Check:
Base class controls sequence; subclasses customize steps [OK]
Hint: Fixed sequence with customizable steps -> Template Method [OK]
Common Mistakes:
Thinking duplicated code is acceptable for reuse
Confusing dynamic step selection with greedy algorithms
4. Given the following Python code using the Template Method Pattern, what is the exact output when calling Tea().prepare_recipe()?
easy
A. Boil water
Steep tea bag
Pour into cup
B. Boil water
Pour into cup
Steep tea bag
C. Steep tea bag
Boil water
Pour into cup
D. Boil water
Steep tea bag
Pour into cup
Add lemon
Solution
Step 1: Trace prepare_recipe steps
Calls boil_water (prints 'Boil water'), then brew (prints 'Steep tea bag'), then pour_in_cup (prints 'Pour into cup').
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