Design Pattern 19: Command Pattern - Complete Guide with Undo/Redo and Remote Control Examples
π Download the complete Design Pattern series code from our design_pattern repository.
π― What is the Command Pattern?
The Command Pattern is a behavioral design pattern that encapsulates a request as an object, allowing you to parameterize clients, queue or log requests, and support undoable operations. It decouples the object that invokes the operation from the one that knows how to perform it.
Key Benefits:
- β Decouples sender and receiver
- β Supports undo/redo
- β Flexible command history
- β Extensible for new commands
- β Centralizes control logic
π Real-World Problem: Music Player Remote Control
Suppose you are building a music player remote control system with the following requirements:
- Users can control play, pause, and stop via remote
- Support for undo (e.g., undo pause resumes play)
- Button actions should be flexible for future features (e.g., next, repeat)
Business Rules:
- All actions are encapsulated as command objects
- Remote control manages command history for undo
- Easy to add new commands without changing existing code
ποΈ Object-Oriented Analysis (OOA)

Identified Forces:
- High coupling - Client must know all device details
- Lack of flexibility - Hard to add new devices or actions
- Undo/redo complexity - No unified way to manage history
π‘ Command Pattern Solution
By encapsulating actions as command objects, we decouple the invoker from the receiver and enable flexible, extensible control.

Command Pattern Components:
- Receiver - Executes the actual operation
- Command Interface - Defines execute/undo
- Concrete Command - Implements specific actions
- Invoker - Triggers commands and manages history
- Client - Sets up relationships
π οΈ Implementation: Music Player Remote Control

1. Receiver: Music Player
class MusicPlayer {
fun play() { println("Music is playing") }
fun pause() { println("Music is paused") }
fun stop() { println("Music is stopped") }
}
2. Command Interface
interface Command {
fun execute()
fun undo()
}
3. Concrete Commands
class PlayCommand(private val player: MusicPlayer) : Command {
override fun execute() { player.play() }
override fun undo() { player.pause() }
}
class PauseCommand(private val player: MusicPlayer) : Command {
override fun execute() { player.pause() }
override fun undo() { player.play() }
}
class StopCommand(private val player: MusicPlayer) : Command {
override fun execute() { player.stop() }
override fun undo() { println("Cannot undo stop") }
}
4. Invoker: Remote Control
class RemoteControl {
private val commandHistory = mutableListOf<Command>()
fun pressButton(command: Command) {
command.execute()
commandHistory.add(command)
}
fun pressUndo() {
if (commandHistory.isNotEmpty()) {
val lastCommand = commandHistory.removeLast()
lastCommand.undo()
} else {
println("No command to undo")
}
}
}
5. Client Code
fun main() {
val player = MusicPlayer()
val playCommand = PlayCommand(player)
val pauseCommand = PauseCommand(player)
val stopCommand = StopCommand(player)
val remoteControl = RemoteControl()
remoteControl.pressButton(playCommand)
remoteControl.pressButton(pauseCommand)
remoteControl.pressUndo()
remoteControl.pressButton(stopCommand)
remoteControl.pressUndo()
}
Expected Output:
Music is playing
Music is paused
Music is playing
Music is stopped
π Command Pattern vs Alternative Approaches
Approach | Pros | Cons |
---|---|---|
Command Pattern | β
Decouples sender/receiver β Undo/redo support | β More classes β Command management overhead |
Direct Calls | β Simple for small apps | β High coupling β No undo/redo |
Event Bus | β Decoupled communication | β Harder to trace logic β Global state |
π― When to Use the Command Pattern
β Perfect For:
- Remote controls (music, TV, smart home)
- Undo/redo systems
- Macro recording/playback
- Task scheduling and queuing
- GUI button actions
β Avoid When:
- Simple, one-off actions
- Small, stateless systems
π§ Advanced Command Pattern Implementations
- Macro commands (batch actions)
- Command logging and replay
- Asynchronous command execution
- Command queues and scheduling
π Real-World Applications
- Remote controls (TV, music, smart home)
- Text editors (undo/redo)
- Transaction management
- Workflow engines
π¨ Common Pitfalls and Best Practices
- Avoid excessive command classes (use parameterized commands)
- Document command history logic
- Use clear naming for commands
π Related Articles
- Design Pattern 1: Object-Oriented Concepts
- Design Pattern 2: Design Principles
- Memento Pattern
- State Pattern
- Observer Pattern
β Conclusion
Through the Command Pattern, we successfully decoupled operations, enabled undo/redo, and made the system more flexible and extensible.
Key Advantages:
- π― Decouples sender and receiver
- π§ Undo/redo support
- π Flexible command history
- π‘οΈ Maintainability
- β‘ Extensibility
Design Principles Followed:
- Single Responsibility Principle (SRP): Each command has one responsibility
- Open-Closed Principle (OCP): Add new commands easily
- Donβt Repeat Yourself (DRY): Centralize command logic
Perfect For:
- Remote controls
- Undo/redo systems
- Workflow engines
The Command Pattern provides an elegant solution for flexible, extensible control in modern applications!
π‘ Pro Tip: Use macro commands and command logging for advanced automation and debugging.
π Stay Updated: Follow our Design Pattern series for more software architecture insights!
Enjoy Reading This Article?
Here are some more articles you might like to read next: