Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Action, Transaction
Sometimes it's necessary to issue requests to objects without knowing anything about the operation being requested or the receiver of the request. For example, user interface toolkits include objects like buttons and menus that carry out a request in response to user input. But the toolkit can't implement the request explicitly in the button or menu, because only applications that use the toolkit know what should be done on which object. As toolkit designers we have no way of knowing the receiver of the request or the operations that will carry it out.
The Command pattern lets toolkit objects make requests of unspecified application objects by turning the request itself into an object. This object can be stored and passed around like other objects. The key to this pattern is an abstract Command class, which declares an interface for executing operations. In the simplest form this interface includes an abstract Execute operation. Concrete Command subclasses specify a receiver-action pair by storing the receiver as an instance variable and by implementing Execute to invoke the request. The receiver has the knowledge required to carry out the request.
Menus can be implemented easily with Command objects. Each choice in a Menu is an instance of a MenuItem class. An Application class creates these menus and their menu items along with the rest of the user interface. The Application class also keeps track of Document objects that a user has opened.
The application configures each MenuItem with an instance of a concrete Command subclass. When the user selects a MenuItem, the MenuItem calls Execute on its command, and Execute carries out the operation. MenuItems don't know which subclass of Command they use. Command subclasses store the receiver of the request and invoke one or more operations on the receiver.
For example, PasteCommand supports pasting text from the clipboard into a Document. PasteCommand's receiver is the Document object it is supplied upon instantiation. The Execute operation invokes Paste on the receiving Document.
OpenCommand's Execute operation is different: it prompts the user for a document name, creates a corresponding Document object, adds the document to the receiving application, and opens the document.
Sometimes a MenuItem needs to execute a sequence of commands. For example, a MenuItem for centering a page at normal size could be constructed from a CenterDocumentCommand object and a NormalSizeCommand object. Because it's common to string commands together in this way, we can define a MacroCommand class to allow a MenuItem to execute an open-ended number of commands. MacroCommand is a concrete Command subclass that simply executes a sequence of Commands. MacroCommand has no explicit receiver, because the commands it sequences define their own receiver.
In each of these examples, notice how the Command pattern decouples the object that invokes the operation from the one having the knowledge to perform it. This gives us a lot of flexibility in designing our user interface. An application can provide both a menu and a push button interface to a feature just by making the menu and the push button share an instance of the same concrete Command subclass. We can replace commands dynamically, which would be useful for implementing context-sensitive menus. We can also support command scripting by composing commands into larger ones. All of this is possible because the object that issues a request only needs to know how to issue it; it doesn't need to know how the request will be carried out.
Use the Command pattern when you want to
The following diagram shows the interactions between these objects. It illustrates how Command decouples the invoker from the receiver (and the request it carries out).
The Command pattern has the following consequences:
Consider the following issues when implementing the Command pattern:
To support one level of undo, an application needs to store only the command that was executed last. For multiple-level undo and redo, the application needs a history list of commands that have been executed, where the maximum length of the list determines the number of undo/redo levels. The history list stores sequences of commands that have been executed. Traversing backward through the list and reverse-executing commands cancels their effect; traversing forward and executing commands reexecutes them.
An undoable command might have to be copied before it can be placed on the history list. That's because the command object that carried out the original request, say, from a MenuItem, will perform other requests at later times. Copying is required to distinguish different invocations of the same command if its state can vary across invocations.
For example, a DeleteCommand that deletes selected objects must store different sets of objects each time it's executed. Therefore the DeleteCommand object must be copied following execution, and the copy is placed on the history list. If the command's state never changes on execution, then copying is not requiredonly a reference to the command need be placed on the history list. Commands that must be copied before being placed on the history list act as prototypes (see Prototype (117)).
The C++ code shown here sketches the implementation of the Command classes
in the Motivation section. We'll define OpenCommand
,
PasteCommand
, and MacroCommand
. First the
abstract Command
class:
class Command { public: virtual ~Command(); virtual void Execute() = 0; protected: Command(); };
OpenCommand
opens a document whose name is supplied by the
user. An OpenCommand
must be passed an
Application
object in its constructor. AskUser
is an
implementation routine that prompts the user for the name of the
document to open.
class OpenCommand : public Command { public: OpenCommand(Application*); virtual void Execute(); protected: virtual const char* AskUser(); private: Application* _application; char* _response; }; OpenCommand::OpenCommand (Application* a) { _application = a; } void OpenCommand::Execute () { const char* name = AskUser(); if (name != 0) { Document* document = new Document(name); _application->Add(document); document->Open(); } }
A PasteCommand
must be passed a Document
object as
its receiver. The receiver is given as a parameter to PasteCommand
's
constructor.
class PasteCommand : public Command { public: PasteCommand(Document*); virtual void Execute(); private: Document* _document; }; PasteCommand::PasteCommand (Document* doc) { _document = doc; } void PasteCommand::Execute () { _document->Paste(); }
For simple commands that aren't undoable and don't require arguments,
we can use a class template to parameterize the command's receiver.
We'll define a template subclass SimpleCommand
for such
commands. SimpleCommand
is parameterized by the
Receiver
type and maintains a binding between a receiver object
and an action stored as a pointer to a member function.
template <class Receiver> class SimpleCommand : public Command { public: typedef void (Receiver::* Action)(); SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a) { } virtual void Execute(); private: Action _action; Receiver* _receiver; };
The constructor stores the receiver and the action in the corresponding
instance variables. Execute
simply applies the action to the
receiver.
template <class Receiver> void SimpleCommand<Receiver>::Execute () { (_receiver->*_action)(); }
To create a command that calls Action
on an instance of class MyClass
, a client simply writes
MyClass* receiver = new MyClass; // ... Command* aCommand = new SimpleCommand<MyClass>(receiver, &MyClass::Action); // ... aCommand->Execute();
Keep in mind that this solution only works for simple commands. More
complex commands that keep track of not only their receivers but also
arguments and/or undo state require a Command
subclass.
A MacroCommand
manages a sequence of subcommands and provides
operations for adding and removing subcommands. No explicit receiver
is required, because the subcommands already define their receiver.
class MacroCommand : public Command { public: MacroCommand(); virtual ~MacroCommand(); virtual void Add(Command*); virtual void Remove(Command*); virtual void Execute(); private: List<Command*>* _cmds; };
The key to the MacroCommand
is its Execute
member
function. This traverses all the subcommands and performs
Execute
on each of them.
void MacroCommand::Execute () { ListIterator<Command*> i(_cmds); for (i.First(); !i.IsDone(); i.Next()) { Command* c = i.CurrentItem(); c->Execute(); } }
Note that should the MacroCommand
implement an
Unexecute
operation, then its subcommands must be
unexecuted in reverse order relative to Execute
's
implementation.
Finally, MacroCommand
must provide operations to manage its
subcommands. The MacroCommand
is also responsible for
deleting its subcommands.
void MacroCommand::Add (Command* c) { _cmds->Append(c); } void MacroCommand::Remove (Command* c) { _cmds->Remove(c); }
Perhaps the first example of the Command pattern appears in a paper by Lieberman [Lie85]. MacApp [App89] popularized the notion of commands for implementing undoable operations. ET++ [WGM88], InterViews [LCI+92], and Unidraw [VL90] also define classes that follow the Command pattern. InterViews defines an Action abstract class that provides command functionality. It also defines an ActionCallback template, parameterized by action method, that can instantiate command subclasses automatically.
The THINK class library [Sym93b] also uses commands to support undoable actions. Commands in THINK are called "Tasks." Task objects are passed along a Chain of Responsibility (223) for consumption.
Unidraw's command objects are unique in that they can behave like messages. A Unidraw command may be sent to another object for interpretation, and the result of the interpration varies with the receiving object. Moreover, the receiver may delegate the interpretation to another object, typically the receiver's parent in a larger structure as in a Chain of Responsibility. The receiver of a Unidraw command is thus computed rather than stored. Unidraw's interpretation mechanism depends on run-time type information.
Coplien describes how to implement functors, objects that
are functions, in C++ [Cop92]. He achieves a degree of
transparency in their use by overloading the function call operator
(operator()
). The Command pattern is different; its focus
is on maintaining a binding between a receiver and a function
(i.e., action), not just maintaining a function.
A Composite (163) can be used to implement MacroCommands.
A Memento (283) can keep state the command requires to undo its effect.
A command that must be copied before being placed on the history list acts as a Prototype (117).