Bird
Raised Fist0

Command Pattern - Encapsulating Operations for Undo, Redo, Queue

LLDeasyInterview freq: common
Choose your preparation mode4 modes available
🏗️
Command Pattern - Encapsulating Operations for Undo, Redo, Queue
Encapsulate requests as objects to support undo, redo, and queuing operations.
easyDesign Patterns - AppliedFreq: common
Prerequisites:OOP BasicsJava concurrency basicsDesign patterns fundamentals
⚙️
Functional Requirements
  • Commands must encapsulate all information needed to perform an action.
  • Invoker must maintain a history stack for undo and redo operations.
  • Invoker must support queuing commands for sequential execution.
  • Commands must support execute(), undo(), and redo() methods.
  • System must be thread-safe to handle concurrent command submissions.
  • Extensibility to add new command types without modifying existing code.
  • Invoker must reject undo if no commands are available to undo.
👤
Use Cases
  • Client creates concrete Command objects encapsulating specific operations.
  • Invoker executes commands and maintains history for undo/redo.
  • Invoker queues commands for asynchronous execution.
  • Invoker supports undo and redo operations on executed commands.
  • Receiver performs the actual business logic when commands are executed.
  • System handles concurrent command execution requests safely.
🧑‍💻
Actors
  • Client
  • Invoker
  • Command
  • Receiver
❓
Clarifying Questions to Ask
  • How many threads will submit commands concurrently to the Invoker?
  • Should the command queue support prioritization or only FIFO?
  • Is there a maximum size for the undo/redo history stacks?
  • Are commands allowed to be executed asynchronously or only synchronously?
  • What happens if undo or redo is called with no commands available?
  • Should commands be idempotent or can they have side effects?
  • Is there a pricing or resource cost associated with command execution?
Out of Scope
✗ Persistence of command history across application restarts.
✗ Distributed command execution across multiple machines.
✗ Complex transaction management beyond simple undo/redo.
⚡
QPS Calculation
write qps1000
read qps5000
peak qps7000
🖥️
Server Estimate
3
Assuming moderate command submission rate with concurrency, 3 servers provide redundancy and load balancing.
🔢
Enumerations
enum CommandStatus
PENDING | EXECUTED | UNDONE | REDONE | FAILED
Tracks the lifecycle state of a Command instance to prevent invalid operations like redo before execute. Enums prevent typo bugs and enable exhaustive switch statements.
enum InvokerMode
SYNCHRONOUS | ASYNCHRONOUS
Defines whether the Invoker executes commands immediately or queues them for asynchronous execution. Using enum avoids string constants and supports type safety.
📦
Command
1 fields
protected volatile CommandStatus status
Abstract base class defining the contract for concrete commands, holding command state and providing execute, undo, and redo behaviors.
public abstract void execute() throws CommandExecutionExceptionExecutes the command's action. Must be implemented by concrete subclasses.
public abstract void undo() throws CommandExecutionExceptionReverts the command's action. Must be implemented by concrete subclasses.
public synchronized void redo() throws CommandExecutionExceptionRe-executes the command after an undo. Default implementation calls execute and updates status.
public CommandStatus getStatus()Returns the current status of the command.
📦
ConcreteCommand
2 fields
private Receiver receiverprivate Object commandData
Implements a specific command by delegating to a Receiver with encapsulated data.
public synchronized void execute() throws CommandExecutionExceptionPerforms the operation by invoking the receiver with commandData, sets status to EXECUTED.
public synchronized void undo() throws CommandExecutionExceptionReverts the operation by invoking receiver's undo logic, sets status to UNDONE.
📦
Receiver
1 fields
private volatile Object state
Performs the actual business logic operations requested by commands.
public synchronized void action(Object data)Performs the operation using the provided data.
public synchronized void revertAction(Object data)Reverts the operation using the provided data.
📦
Invoker
6 fields
private final Deque<Command> undoStackprivate final Deque<Command> redoStackprivate final BlockingQueue<Command> commandQueueprivate final InvokerMode modeprivate volatile boolean isRunningprivate final Thread workerThread
Accepts commands from clients, executes them, manages undo/redo stacks, and optionally queues commands for asynchronous execution.
public synchronized void invoke(Command command) throws CommandExecutionExceptionExecutes or queues the command depending on mode, updates undo stack.
public synchronized void undo() throws CommandExecutionExceptionUndoes the last executed command if available.
public synchronized void redo() throws CommandExecutionExceptionRedoes the last undone command if available.
public synchronized void start()Starts the worker thread if in asynchronous mode.
public synchronized void stop()Stops the worker thread gracefully.
📦
CommandExecutionException
2 fields
private String messageprivate Throwable cause
Custom checked exception to indicate failures during command execution, undo, or redo.
public CommandExecutionException(String message, Throwable cause)Initializes exception with message and cause.
🔗
Relationships
ConcreteCommand──►Receiver[1]
associationReceiver exists independently of ConcreteCommand
Invoker◇──►Command[0..*]
aggregationCommands can exist independently; Invoker aggregates commands in history stacks
Command──►CommandStatus[1]
associationEnum instance shared, no lifecycle ownership
🗄️
command_history
PK: user_id
command_id UUIDcommand_type STRINGstatus STRINGpayload BLOB
Stores command execution history per user for undo/redo.
🗄️
command_queue
PK: queue_id
command_id UUIDcommand_data BLOBstatus STRING
Stores queued commands for asynchronous execution.
🔌
POST
/commands/execute
Request
{
  "commandType": "string",
  "payload": "object"
}
Response
{
  "status": "string",
  "message": "string"
}
Executes a command immediately or queues it based on Invoker mode.
🔌
POST
/commands/undo
Request
{
  "userId": "string"
}
Response
{
  "status": "string",
  "message": "string"
}
Undoes the last executed command for the user if available.
🔌
POST
/commands/redo
Request
{
  "userId": "string"
}
Response
{
  "status": "string",
  "message": "string"
}
Redoes the last undone command for the user if available.

Practice

(1/5)
1. In designing an Elevator system using the State pattern, should the ElevatorState be an abstract class or an interface, and why?
easy
A. Interface, because states only define behavior without shared implementation
B. Abstract class, because states share common code and state variables
C. Abstract class, because it enforces a single inheritance hierarchy
D. Interface, because it allows multiple inheritance of state behaviors

Solution

  1. Step 1: Analyze shared code in states

    Elevator states often share common fields (e.g., reference to context) and helper methods, which favors an abstract class.
  2. Step 2: Evaluate interface limitations

    Interfaces cannot hold state or provide default implementations (prior to Java 8), so they are less suitable if shared code exists.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Shared code and state -> abstract class [check]
Hint: Shared code and state -> abstract class; behavior only -> interface [check]
Common Mistakes:
2. In the ParkingLot class, if parkVehicle() is not synchronized, which exact sequence of Thread 1 and Thread 2 causes double-booking of the same spot?
medium
A. Thread 1 checks spot availability -> true, context switch; Thread 2 checks same spot -> true; both assign spot simultaneously.
B. Thread 1 releases spot, Thread 2 immediately parks vehicle on it.
C. Thread 1 parks vehicle, Thread 2 parks vehicle after Thread 1 finishes.
D. Thread 1 and Thread 2 both wait on a lock, then park sequentially.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available but before marking it occupied, Thread 2 also reads it as available.
  2. Step 2: Both threads proceed to assign the same spot concurrently

    Because no lock protects the check and assignment, both parkVehicle calls succeed, causing double-booking.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without lock -> double-booking race [check]
Common Mistakes:
3. In the OrderBook class, the placeOrder() method checks if the order slot is available and then books it. Without synchronization on placeOrder(), which exact sequence of thread interleaving causes double-booking of the same slot?
medium
A. Thread 1 reads slot as occupied, Thread 2 reads slot as free, both proceed to book.
B. Thread 1 checks slot availability -> free, context switch before booking; Thread 2 checks slot availability -> free, both book same slot.
C. Thread 1 books slot, Thread 2 waits for lock and then books the same slot after release.
D. Thread 1 and Thread 2 both crash before booking, leaving slot unbooked.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads slot as free but is preempted before booking; Thread 2 also reads free and proceeds to book.
  2. Step 2: Why this causes double-booking

    Both threads believe the slot is free and book it concurrently, causing inconsistent state and double-booking.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without lock -> double-booking [check]
Hint: Check slot free -> context switch -> both book same slot [check]
Common Mistakes:
4. In a parking lot system, if parkVehicle() is not synchronized, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 parks vehicle, Thread 2 parks vehicle simultaneously without checking availability.
B. Thread 1 updates spot status after parking, Thread 2 reads stale status and parks again.
C. Thread 1 releases spot, Thread 2 immediately parks without synchronization.
D. Thread 1 checks spot availability -> true, context switch; Thread 2 checks availability -> true; both park concurrently.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available and is preempted before marking it occupied. Thread 2 also reads available and proceeds to park.
  2. Step 2: Confirm both threads park concurrently causing double-booking

    Both threads believe the spot is free and park, leading to inconsistent state and double-booking.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without sync -> double-booking race condition [check]
Common Mistakes:
5. In a parking lot system using the Strategy pattern, if parkVehicle() is NOT synchronized, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 parks vehicle; Thread 2 waits until Thread 1 finishes, then parks another vehicle.
B. Thread 1 checks spot availability -> true, context switch; Thread 2 checks spot availability -> true; both park simultaneously.
C. Thread 1 parks vehicle; Thread 2 reads spot as occupied and waits.
D. Thread 1 and Thread 2 both crash due to deadlock.

Solution

  1. Step 1: Identify check-then-act race condition

    Without synchronization, Thread 1 reads spot as available but before marking it occupied, context switches to Thread 2 which also reads available.
  2. Step 2: Both threads proceed to park, causing double-booking

    Because the state update is not atomic, both threads believe the spot is free and park simultaneously.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap -> double-booking due to unsynchronized parkVehicle() [check]
Hint: Check availability -> context switch -> both park -> double-booking [check]
Common Mistakes:
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