study of Design Patterns (1994). Diagrams are scanned from original text
Encapsulate request as an object, thereby letting you parameterize clients with difference 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.
Here, Menuitem
will have reference to a Command
's instance, command
. command.execute()
will be called when this MenuItem
is clicked.
But MenuItem
doesn't need to know about details of Execute()
. It suffices to know that it will cause some expected consequence.
Also, different kind of MenuItem
will have reference to different type of Command
objects. But it doesn't matter. MenuItem
will just call MenuItem.Command.Execute()
.
Then Command
subclasses will store reference to receiver of the request, and invoke operations on the receiver as needed. Ex. a PasteCommand
could have reference to a Document
object, which is supplied to PasteCommand
at instantiation. When Execute()
is called on PasteCommand
, it will run some operations (maybe Document.Paste()
) to receiver.
However, OpenCommand
's Execute()
will do different operations like below.
And sometimes, a MenuItem
needs to execute a sequence of commands. Then we can create a macro, MacroCommand
, which has a sequence of Command
objects that runs in sequence. But this MacroCommand
needn't keep a reference to receiver of commands, as each Command
will have that.
In each of these examples, notice how the Command pattern decouples operation invoker object from operation performer object. This will give a lot of flexibility in desiging UI. (I guess this is lot like ReactJS hooks?). We can also change commands dynamically, especially for 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:
Command.Execute()
can store state for reversing its effects in the command itself. then Command
interface must have sth like Unexecute()
. Executed command are stored in a history list. You can have unlimited-level undo and redo if you want to, by traversing this history list of commands.Command
interface w/ load/store operations, so you can keep a persistent log of changes. When it crashes, reload logged commands from disk and reexecute them in order. I guess this works in simple cases?Command
ConcreteCommand
(PasteCommand
, OpenCommand
)Receiver
object and an action.Execute()
by invoking necessary operations on Receiver
.Client
(Application
)ConcreteCommand
object and sets its receiver.Invoker
(MenuItem
)Receiver
(Document
, Application
)ConcreteCommand
object and specifies its receiver.Invoker
object stores the ConcreteCommand
object.Invoker.Command.Execute()
. If commands are undoable, ConcreteCommand
will store state for undoing the command prior to invoking Execute()
ConcreteCommand
object invokes operations on its receiver to carry out the request.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).
MacroCommand
class described earlier. In general, composite commands are an instance of the Composite Pattern.Unexecute()
, Undo()
). A ConcreteCommand
class might need to store additional state to do so. This state can include:Receiver
object, which actually carries out operations in response to the request.Usually history list of commands that have been executed is kept, w/ reasonable size limit. Traverse thru the list and call Execute()
/Unexecute()
as needed.
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 invoacations 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 doesn't change, keeping a reference to it in the object is enough. Commands that must be copied before being placed on the history list act as prototypes.
Avoiding error accumulation in the undo process. Hysterisis can be a problem in ensuring a reliable semantics-preserving undo/redo mechanism. Erros 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 therefor to store more info in the command to ensure that objects are restored to their original state. The Memento Pattern can be applied to give the command access to this info w/o exposing the internals of other objects.
Using C++ Templates. For commands that aren't undoable && don't require arguments. Shown in Sample Code section.
Book C++, me python.
not sure what using C++ templates meant above.
some papers, old examples. someting about functors, bojects that are functions.
Composite Pattern to implement MacroCommands
Memento Pattern to keep state the command requires for undoing operations.
Prototype Pattern for commands that must be copied before being placed on the history list.