Command Pattern
Represent a request as an object so creating it and executing it can happen at different times.
· Last updated: 2026-10-25 · 5 min read
Overview
A button may queue an edit instead of applying it immediately. The queued work needs to retain both its target and its arguments.
Command represents that request as an object. The receiver performs the actual work; the invoker calls Execute(). Here those roles belong to a text buffer and a command queue.
UML
CommandQueue stores ICommand objects. AppendText holds a TextBuffer and a string, then calls Append() when Execute() runs.
History
GoF cataloged Command as a behavioral pattern in Design Patterns in 1994. Object requests can be managed in structures such as queues and execution histories.1
Why use it?
A queue can manage execution order without knowing each edit. Callers can also create a request now and execute it later.
A command object does not supply undo or duplicate prevention automatically. Undo needs saved state or an inverse operation; commands with external effects also need an explicit re-execution policy.
Example
Construct and execute a command that appends text.
using System;
using System.Collections.Generic;
using System.Linq;
var buffer = new TextBuffer();
ICommand command = new AppendText(buffer, "A");
command.Execute();
Console.WriteLine(buffer.Text);
sealed class TextBuffer
{
public string Text { get; private set; } = "";
public void Append(string value) => Text += value;
}
interface ICommand { void Execute(); }
// Retain the receiver and arguments in the command for later execution.
sealed class AppendText(TextBuffer buffer, string value) : ICommand
{
public void Execute() => buffer.Append(value);
}
// The queue manages order through execute without knowing the details of an edit.
sealed class CommandQueue
{
private readonly Queue<ICommand> commands = new();
public void Add(ICommand command) => commands.Enqueue(command);
public void RunAll()
{
while (commands.TryDequeue(out var command)) command.Execute();
}
}
AAppendText retains both the buffer and the string it will append.
An extended example
Queue two commands. The buffer stays empty until RunAll() executes them in order.
using System;
using System.Collections.Generic;
using System.Linq;
var buffer = new TextBuffer();
var queue = new CommandQueue();
queue.Add(new AppendText(buffer, "A"));
queue.Add(new AppendText(buffer, "B"));
Console.WriteLine("before: [" + buffer.Text + "]");
queue.RunAll();
Console.WriteLine("after: [" + buffer.Text + "]");
sealed class TextBuffer
{
public string Text { get; private set; } = "";
public void Append(string value) => Text += value;
}
interface ICommand { void Execute(); }
// Retain the receiver and arguments in the command for later execution.
sealed class AppendText(TextBuffer buffer, string value) : ICommand
{
public void Execute() => buffer.Append(value);
}
// The queue manages order through execute without knowing the details of an edit.
sealed class CommandQueue
{
private readonly Queue<ICommand> commands = new();
public void Add(ICommand command) => commands.Enqueue(command);
public void RunAll()
{
while (commands.TryDequeue(out var command)) command.Execute();
}
}
before: []
after: [AB]This is a synchronous in-memory queue, without persistence, crash recovery or resume-after-failure behavior. Executing the same AppendText again appends its text again.
TypeScript example
The buffer performs edits, AppendText retains a target and arguments, and the queue controls execution order. This lets a request be created before the edit actually runs.
class TextBuffer {
#text = "";
get text(): string { return this.#text; }
append(value: string): void { this.#text += value; }
}
interface ICommand { execute(): void; }
// Retain the receiver and arguments in the command for later execution.
class AppendText implements ICommand {
constructor(private readonly buffer: TextBuffer, private readonly value: string) {}
execute(): void { this.buffer.append(this.value); }
}
// The queue manages order through execute without knowing the details of an edit.
class CommandQueue {
private readonly commands: ICommand[] = [];
add(command: ICommand): void { this.commands.push(command); }
runAll(): void {
for (;;) {
const command = this.commands.shift();
if (command === undefined) return;
command.execute();
}
}
}
const buffer = new TextBuffer();
const queue = new CommandQueue();
queue.add(new AppendText(buffer, "A"));
queue.add(new AppendText(buffer, "B"));
console.log("before: " + buffer.text);
queue.runAll();
console.log("after: " + buffer.text);before:
after: ABEnqueueing does not edit the buffer. This synchronous in-memory queue supplies no undo, recovery after failure, or duplicate-execution prevention.
Related reading
Each C# example is a separate .NET 10 console program.
Compile the TypeScript examples for ES2022 or later and run the emitted JavaScript with Node.js.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Behavioral patterns, Command. ↩