Command is a behavioral design pattern that lets 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 are working on a new text editor app. You just finished creating a new
Button class, which can be used for toolbar buttons, as well as for generic buttons in dialogs.
While all these buttons may look similar, they do different things. This raises a dilemma of where to put the code for various click handlers of individual buttons. The simplest solution is to create tons of subclasses for each button and move there all of the operations that have to be executed after a button gets clicked.
But shortcomings of this idea become apparent very fast. Firstly, you end up with an enormous amount of subclasses. Secondly, the GUI code becomes awkwardly dependent on a volatile code of business logic.
And here is 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 will 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 is easy to make various commands to follow common interface now that their primary method does not 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 will not 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 will 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 is 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 declares the common interface for all concrete commands. The absolute minimum is a single method to run the actual operation.
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.
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 is 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 is 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
Declare 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 do not 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.
Command establishes a one-directional connection from senders to receivers.
Mediator has senders and receivers reference each other indirectly.
Observer passes a request to all concerned receivers at the same time but allows them to subscribe and unsubscribe from receiving further requests dynamically.
But there's another approach, where the request itself is a Command object, passed along a chain of objects. In this case, the same operation may be executed over a lot of different contexts, represented as chained objects.
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.