CodeOath
← All posts
Architecture & Patterns80 min total · 26 parts

ACID, SOLID, and Common Design Patterns: A Software Design Reference

Contents — Part 21 of 26: Behavioral Patterns: Command
Part 21 of 26 · ~1 min

Behavioral Patterns: Command

Command encapsulates a request as a standalone object, so it can be queued, logged, undone, or passed around independently of whatever code originally triggered it.

public interface ICommand
{
    void Execute();
    void Undo();
}

public class AddTextCommand : ICommand
{
    private readonly StringBuilder _document;
    private readonly string _text;
    public AddTextCommand(StringBuilder document, string text)
    {
        _document = document;
        _text = text;
    }
    public void Execute() => _document.Append(_text);
    public void Undo() => _document.Remove(_document.Length - _text.Length, _text.Length);
}

public class CommandHistory
{
    private readonly Stack<ICommand> _history = new();

    public void Execute(ICommand command)
    {
        command.Execute();
        _history.Push(command);
    }

    public void UndoLast()
    {
        if (_history.Count > 0) _history.Pop().Undo();
    }
}

var document = new StringBuilder();
var history = new CommandHistory();
history.Execute(new AddTextCommand(document, "Hello"));
history.Execute(new AddTextCommand(document, ", world"));
history.UndoLast(); // removes ", world" — document is back to "Hello"

This is exactly how undo/redo stacks in editors are implemented, and the same shape underlies background job queues — a SendEmailCommand or ProcessPaymentCommand object can be serialized, stored, and executed later by a worker process with no knowledge of where or why it was originally created.