In real applications, user or system actions can quickly become complex: save, undo, redo, task scheduling, macros, or processing queues. If these actions are implemented directly in the UI or in the classes that trigger them, the application becomes difficult to extend and maintain.
Command Pattern solves this problem by transforming each action into an independent object that contains all the information necessary to be executed. Thus, the request is completely decoupled from the object that executes it.
What problem does the Command Pattern solve?
Without Command Pattern:
-
The UI knows the business logic
-
Actions cannot be easily undone or redone (undo/redo)
-
It is difficult to add new actions without modifying existing code
With Command Pattern:
-
Actions are standalone objects
-
You can store, log, or re-execute commands
-
You can build undo/redo mechanisms or macros
Command Pattern Structure
The pattern consists of four main components:
-
Command – interface or abstract class that defines the Execute() method
-
ConcreteCommand – implements the concrete command
-
Receiver – the object that actually performs the action
-
Invoker – the object that triggers the command
Optional:
-
Undo() – for reversing the action
-
Command History – for storing executed commands
Conceptual Diagram (description)
Invoker → Command → Receiver
The invoker does not know what the command does, only that it executes it.
Simple example in C#
1. Command Interface
public interface ICommand
{
void Execute();
}
2. Receiver – business logic
public class Light
{
public void TurnOn()
{
Console.WriteLine("The light has been turned on");
}
public void TurnOff()
{
Console.WriteLine("The light has been turned off");
}
}
3. Concrete Commands
public class TurnOnLightCommand : ICommand
{
private readonly Light _light;
public TurnOnLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
}
public class TurnOffLightCommand : ICommand
{
private readonly Light _light;
public TurnOffLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
}
4. Invoker
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command)
{
_command = command;
}
public void PressButton()
{
_command.Execute();
}
}
5. Usage
var light = new Light();
var turnOn = new TurnOnLightCommand(light);
var turnOff = new TurnOffLightCommand(light);
var remote = new RemoteControl();
remote.SetCommand(turnOn);
remote.PressButton();
remote.SetCommand(turnOff);
remote.PressButton();
Undo / Redo Implementation
Command Pattern becomes extremely powerful when we add support for undo.
Extending the interface
public interface ICommand
{
void Execute();
void Undo();
}
Command with Undo
public class TurnOnLightCommand : ICommand
{
private readonly Light _light;
public TurnOnLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
public void Undo()
{
_light.TurnOff();
}
}
Command History
public class CommandManager
{
private readonly Stack<ICommand> _history = new();
public void ExecuteCommand(ICommand command)
{
command.Execute();
_history.Push(command);
}
public void Undo()
{
if (_history.Any())
{
var command = _history.Pop();
command.Undo();
}
}
}
Real examples where Command Pattern is used
-
Undo / Redo in editors (Word, Photoshop)
-
Menu systems and buttons
-
Task queues and job scheduling
-
Macros (chain execution of commands)
-
CQRS (Command side)
Advantages
✔️ Complete decoupling between UI and business logic
✔️ Extensibility without modifying existing code
✔️ Native support for undo/redo
✔️ Easy testing of individual commands
Disadvantages
❌ Increases the number of classes
❌ Can be overkill for simple applications
Command Pattern vs Strategy Pattern
|
Command |
Strategy |
|---|---|
|
Represents an action |
Represents an algorithm |
|
Can be stored |
Usually not |
|
Supports undo/redo |
No |
|
One-time execution |
Long-term behavior |
Conclusion
Command Pattern is an essential pattern for modern and extensible applications. When actions become independent objects, the application gains flexibility, testability, and control.
If you need undo/redo, processing queues, or macros, Command Pattern is the right choice.