Applied Design Patterns with Java
Behavioral :: Command (233) {C ch 17}
Implementation
Here are implementation issues to consider when using the
Command pattern:
- How intelligent should a Command be? A command can have a wide range of abilities. At one extreme it defines a binding
between a receiver and the delegated actions that carry out the request. At the other extreme it implements everything
itself without delegating to a receiver at all. The latter extreme is useful when you want to define commands that
are independent of existing classes, when no suitable receiver exists, or when a command knows its receiver implicitly.
A command that creates another application window may be just as capable of creating the window as any other object.
Between these extremes are Commands that have enough knowledge to find their receiver dynamically.
- Supporting undo and redo.
Commands
can support undo and redo capabilities if they provide a way to reverse their execution (e.g., an Unexecute or
Undo operation). A ConcreteCommand class might need to store additional state to do so. This state can include
the Receiver object, which actually carries out operations in response to the request, the arguments to the operation
performed on the receiver, and any original values in the Receiver that can change as a result of handling the
request. The Receiver must provide operations that let the Command return the receiver to its prior state. 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 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. A DeleteCommand
that deletes selected objects must store different sets of objects each time it's executed. 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 required—only a reference to the command need be placed on the history
list. A Command
that must be copied before being placed on the history list acts as a Prototype (117).
- Avoiding error accumulation in the undo process. Hysteresis (the
tendency of a system to behave differently depending on the direction of change of an input parameter) can be a
problem in ensuring a reliable, semantics-preserving undo/redo mechanism. Errors can accumulate as Commands are executed, unexecuted, and
reexecuted repeatedly so that an application's state eventually diverges from original values. It may be necessary
to store more information in the Command to ensure that objects are restored to their original state. The Memento (283)
pattern can be applied to give the Command access to this information without exposing the internals of other objects.
- Using C++ templates. This
capability does not currently exist in Java.
Related Patterns
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).
Catalog Behavioral Prev Next