Command
Command is a pattern that turns a request -- 'do this' -- into a standalone object. The side sending the request (Invoker) does not know what the request specifically does or how, and the side performing the work (Receiver) does not know who requested it. Once a request becomes an object, it can be stored, transmitted, undone, and replayed.
βΆArchitecture Diagram
π ProcessDashed line animations indicate the flow direction of data or requests
Consider building a GUI where pressing a button triggers an action. If the action is coded directly inside the button, duplicating the same action for a menu, keyboard shortcut, or context menu means duplicating the code. When an 'undo' feature is needed, the problem grows: you must remember what action ran when, and also hold a way to reverse it. If actions are scattered as function calls, there is no object to serve as a history record.
Command's roots trace back to 1970s operating-system job scheduling. Representing tasks as data structures made it possible to enqueue them, assign priorities, and log them. When the GUI era arrived, the idea moved to the application level. Implementing undo/redo in a text editor required recording each user action in a reversible form, and that recording unit became the Command object. Since GoF formalized it, the structure has been used as a core building block in architectural patterns like message queues, CQRS (Command Query Responsibility Segregation), and event sourcing.
Command has four roles. The Command interface defines execute(). A Concrete Command implements that interface, holding a Receiver reference and the parameters needed for execution. The Invoker receives and stores a Command object, calling execute() at the appropriate moment. The Receiver performs the actual business logic. The flow works like this: the Client creates a Receiver, creates a Command containing that Receiver, and registers it with the Invoker. The Invoker fires execute() on a trigger (button click, schedule, event), and the Command delegates the real work to its internal Receiver. To support undo, an undo() method is added to the Command, and the Invoker pushes executed Commands onto a stack. On an undo request, the top Command is popped and undo() is called. This structure makes extensions like execution-history management, macros (bundling multiple Commands into one), and transactions (execute all or undo all) straightforward.
Command and Strategy both wrap behavior in objects, but the intent differs. Strategy is about 'performing the same task in a different way' by swapping algorithms; Command is about 'recording what was done and controlling it' by objectifying requests. Strategy cares about algorithm replacement; Command cares about execution timing, history, and undo. Confusion with Observer is also possible. Observer propagates a state change to multiple recipients, while Command turns a single request into an object and manages its lifecycle (creation, delivery, execution, undo). They are often used together in event systems: Observer propagates the event, and inside a handler a Command object is executed.
Commonly Compared Concepts
Strategy
A behavioral pattern that encapsulates algorithms and makes them interchangeable at runtime
Command turns a request into an object to control execution timing and history, while Strategy turns an algorithm into an object to swap how the same task is performed.
Observer
A behavioral pattern that automatically propagates state changes to subscribers
Command manages the lifecycle of a single request (execution, undo, queuing), while Observer propagates a single state change to multiple recipients. They are frequently combined in event systems.
Command's most compelling use case is undo/redo. In text editors, graphic editors, and spreadsheets, recording every user operation as a Command means undo is implemented simply by rewinding the stack. It is also valuable for job queues. Serializing requests into Command objects lets them be sent across the network to another server or queued for a worker to process later. When transaction boundaries are needed, multiple Commands are bundled: all execute() or, if any fails, all undo(). The signals for adoption are 'this action must be reversible,' 'this request must be executed later,' or 'action history must be recorded.' If a simple method call suffices and neither history nor undo is needed, there is no reason to introduce Command.