Command is a behavioral design pattern that let you turn a request into stand-alone object, which can be used to parametrize objects with different requests, queue or log requests, and support undoable operations.
Imagine that you're working on a new text editor app. You just finished creating a new
Buttons class that you plan to use for toolbar buttons, as well as for generic buttons in dialogs.
While all these buttons look similar, they do different things. This raises a dilemma of where to put the code for various click handlers. The simplest solution is to create subclasses for each specific button and put operations that have to be executed upon the button click there.
But the shortcomings of this idea become apparent very fast. The first one is that you end up with an enormous amount of subclasses. The second is that code of the GUI becomes awkwardly dependent on the volatile code of business logic.
And here's the ugliest part. Some operations, like copying text, could be invoked from several places. For example, you could press a button on the toolbar, or click the context menu item or just hit
Ctrl+C. When the app had only buttons, the copy operations lived inside a
CopyButton subclass. But now you'll have to duplicate the code of copying operation in two other places.
The Command pattern suggests encapsulating requests into their very own objects called commands. In case if the operation had any parameters, they become fields of a new command class.
Most commands serve as links between clients, which trigger requests and receiving objects, which handle them by performing some operations.
It's easy to make various commands to follow common interface now that their primary method doesn't have any parameters. Usually, a command interface has just one method that looks as
execute(). Once the common interface is created, you can make commands interchangeable in client code which used to be coupled to specific operations.
After applying the Command pattern to our example with the text editor, you won't need tons of
Button subclasses. The base class will get a field for storing a reference to a command object. Instead of performing any real work, a button object will delegate the request to a linked command object upon receiving a click from a user. The command would either execute an operation on its own or delegate it to one of the business logic objects.
You can arrange the code of context menus and hotkeys in a similar way. All of these classes will be delegating work to a single command object shared between them.
Thus, command classes will become convenient middle layer between GUI and business logic classes. And that's only a fraction of all Command's benefits!
Ordering at a restaurant
You enter a restaurant and pick a window seat. A waiter takes your order on a piece of paper, brings to the kitchen and stick it to the wall, under all other orders.
As you probably guessed, the paper ticket serves as a command. It remains in a queue until a chef is ready to serve it. The order contains all relevant information required to cook a dish. It allows a chef to start cooking right away instead of running around clarifying the order details.
Invoker stores a reference to a Command object and uses it when an operation needs to be executed. Invoker works with commands only via their common interface, which usually exposes just a single execution method. Invokers are not responsible for creating command objects. They usually get a pre-created command from the Client via the constructor.
Command defines the common interface for all concrete commands. The absolute minimum is a single method to
Concrete commands implement the actual operations. Some commands can be self-contained and immutable, accepting all necessary data just once, via constructor parameters. Others require a Receiver, an external context object.
Receiver contains business logic or data essential to a particular command. Commands may query these objects for additional info or delegate the entire operation to them. In some cases, receiver's code can be merged into command classes for the sake of simplicity.
Client creates and configures Concrete Command objects. Then passes these objects to appropriate Invokers.
In this example, the Command pattern is used for logging history of executed operations and being able to revert it. Unlike the previous examples, here the application creates new commands each time user interacts with it. Later this helps to list individual commands in the command history.
Before executing the operation, a command creates a backup of the editor's state. After execution, the command puts itself to the history stack.
The client code, such as UI elements, command history and other classes are not coupled to concrete command classes because they all work with commands via the common command interface. This allows adding new commands to the application without changing existing code.
// Abstract command defines the common interface for all concrete commands. abstract class Command is field app: Application field editor: Editor field backup: text constructor Command(app: Application, editor: Editor) is this.app = app this.editor = editor // This abstract command defines undo functionality. To save more complex // editor state, you might need to use the Memento pattern. method backup() is backup = editor.text app.history.push(this) method undo() is editor.text = backup // Main command method stays abstract so that each concrete command could // provide its own implementation. abstract method execute() // Concrete commands. class CopyCommand extends EditorCommand is // The copy operation is not saved to the history since it does not change // editor's state. method execute() is app.clipboard = editor.getSelection() class CutCommand extends EditorCommand is method execute() is // Commands, which change editor's state, will be saved to the history. backup() app.clipboard = editor.getSelection() editor.deleteSelection() class PasteCommand implements Command is method execute() is backup() editor.replaceSelection(app.clipboard) // Global command history is just a stack. class CommandHistory is history: array of Command // Last in... method push(c: Command) is Push command to the end of history array. // ...first out method pop():Command is Get the most recent command from history. // Editor class has an actual text editing operations. It plays the role of // Receiver: all commands end up delegating execution to editor's methods. class Editor is field text: string field cursorX, cursorY, selectionWidth method getSelection() is Return selected text. method deleteSelection() is Delete selected text. method replaceSelection(text) is Insert clipboard contents at current position. // Application class configures object relations. It acts as a Sender–creates // and sends command objects to execute some action. class Application is field clipboard: string field editors: array of Editors field activeEditor: Editor field history: CommandHistory method createUI() is onKeyPress("Ctrl+C", this.getCopyCommand); onKeyPress("Ctrl+X", this.getCutCommand); onKeyPress("Ctrl+V", this.getPasteCommand); onKeyPress("Ctrl+Z", this.undo); // Hotkeys handler creates new command object each time it fires. In this // case, commands could work with multiple editors, but they have the // common clipboard. method getCopyCommand() is return (new CopyCommand(this, activeEditor)).execute() method getCutCommand() is return (new CutCommand(this, activeEditor)).execute() method getPasteCommand() is return (new PasteCommand(this, activeEditor)).execute() // Take the last command from history and run its undo method. Note that we // don't know the type of that command. But we don't have to since command // knows how to undo its own actions. method undo() is command = history.pop() if (command != null) command.undo()
When you want to parameterize objects with actions. For example, when you develop a user interface component, such as a menu, you want your users to be able to configure menu items with actions that will be fired when a menu item is clicked.
The Command pattern turns operations into objects that can be linked from various UI elements. An element delegates the work to command objects instead of doing it by itself. A command executes an operation on its own or passes the call to the appropriate object of business logic.
When you want to queue, schedule, or execute operations remotely.
Like any other object, a command can be serialized, which means converting it to a string. That string can be saved to a file or database and retrieved later to be restored as a command object. But here's more! You could send a serialized command over the network, restore and executed it on a remote server.
When you need to be able to undo operations.
The first thing that you need to be able to revert operations is storing history. Although there are many ways to do this, the Command pattern is perhaps the most popular of all.
The command history is a stack composed of executed command objects. Each command creates a snapshot of the application's state before executing the operation. After the operation is completed, the command puts itself into the history stack. Note that it keeps a backup of the application's state at all times. When undo is required, the program takes the first command from the history stack and restores the snapshot stored in it.
This method has two drawbacks. First, it's not that easy to save an application's state, because some of it can be private. This problem can be mitigated with the Memento pattern.
Secondly, the state backups may consume quite a lot of RAM. Therefore, sometimes you can resort to an alternative implementation, where instead of restoring the past state, the command performs the inverse operation. This option also has a price. The reverse operation is often hard or even impossible to implement.
How to Implement
Define the Command interface with a single
Start extracting operations into new Concrete command classes that follow common Command interface. Transform operation's parameters into fields in concrete command classes. They should be initialized via command's constructor.
Make sure that command has fields for storing references to any Receiver objects it needs to collaborate with. These fields should also get initial values via the constructor.
Identify Invoker classes and give them fields for storing command objects. Invokers should communicate with command objects only using the Command interface. They usually don't create command objects by themselves, but rather get them from the client code.
The main application's code, acting as Client, should create and configure Concrete commands and pass them to the appropriate Invoker objects. Sometimes multiple Invokers can use the same command objects.
Pros and Cons
- Decouples classes that invoke operations from classes that perform them.
- Allows reversal (undo) of operations.
- Allows deferred execution of operations.
- Allows assembling simple commands into larger ones.
- Follows the Open/Closed Principle.
- Increases overall code complexity by creating multiple additional classes.
Relations with Other Patterns
Chain of Responsibility passes a request sequentially along a dynamic chain of potential receivers until one of them handles a request.
Observer passes a request to all concerned receivers at the same time but allows them to subscribe and unsubscribe from receiving further requests dynamically.
Command establishes a one-directional connection from senders to receivers.
Mediator has senders and receivers reference each other indirectly.
Command and Memento can be used together. They can act as magic tokens to be passed around and invoked at a later time. In Command, the token represents a request; in Memento, it represents the internal state of an object at a particular time. Polymorphism is important to Command, but not to Memento because its interface is so narrow that a memento can only be passed as a value.
Command and Strategy are very similar, since both used to parametrize context with some action. Command can be used to convert any operation into an object. Operation's parameters become fields of that object. The conversion allows deferred or remote execution, storing command history, etc.
On ther other hand, the Strategy pattern usually describes different ways of doing the same thing. It helps to interchange these algorythms in a single context class.