📄 pat5b.htm
字号:
<A NAME="supp-undo-redo"></A>
<LI><EM>Supporting undo and redo.</EM>
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</LI>
<A NAME="auto1053"></A>
<P></P>
<UL>
<A NAME="receive-part-command2"></A>
<LI>the Receiver object, which actually carries out operations in
response to the request,</LI>
<A NAME="auto1054"></A>
<P></P>
<A NAME="auto1055"></A>
<LI>the arguments to the operation performed on the receiver, and</LI>
<A NAME="auto1056"></A>
<P></P>
<A NAME="auto1057"></A>
<LI>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.</LI>
</UL>
<A NAME="auto1058"></A>
<P></P>
<A NAME="auto1059"></A>
<P>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 <STRONG>history list</STRONG> 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.</P>
<A NAME="auto1060"></A>
<P>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.</P>
<A NAME="proto-w-command"></A>
<P>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 required—only 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
<A HREF="pat3dfs.htm" TARGET="_mainDisplayFrame">Prototype (117)</A>).</P>
<A NAME="avoid-error"></A>
<LI><EM>Avoiding error accumulation in the undo process.</EM>
Hysteresis 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 therefore to store more information in the command to
ensure that objects are restored to their original state. The
<A HREF="pat5efs.htm" TARGET="_mainDisplayFrame">Memento (283)</A> pattern can be applied to give the command
access to this information without exposing the internals of other
objects.</LI>
<A NAME="auto1061"></A>
<P></P>
<A NAME="template-implement"></A>
<LI><EM>Using C++ templates.</EM>
For commands that (1) aren't undoable and (2) don't require arguments,
we can use C++ templates to avoid creating a Command subclass for
every kind of action and receiver. We show how to do this in the Sample
Code section.</LI>
</OL>
<A NAME="samplecode"><A>
<H2><A HREF="#knownuses"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Sample Code</H2>
<A NAME="auto1062"></A>
<P>The C++ code shown here sketches the implementation of the Command classes
in the Motivation section. We'll define <CODE>OpenCommand</CODE>,
<CODE>PasteCommand</CODE>, and <CODE>MacroCommand</CODE>. First the
abstract <CODE>Command</CODE> class:</P>
<A NAME="auto1063"></A>
<PRE>
class Command {
public:
virtual ~Command();
virtual void Execute() = 0;
protected:
Command();
};
</PRE>
<A NAME="auto1064"></A>
<P><CODE>OpenCommand</CODE> opens a document whose name is supplied by the
user. An <CODE>OpenCommand</CODE> must be passed an
<CODE>Application</CODE> object in its constructor. <CODE>AskUser</CODE> is an
implementation routine that prompts the user for the name of the
document to open.</P>
<A NAME="auto1065"></A>
<PRE>
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();
}
}
</PRE>
<A NAME="auto1066"></A>
<P>A <CODE>PasteCommand</CODE> must be passed a <CODE>Document</CODE> object as
its receiver. The receiver is given as a parameter to <CODE>PasteCommand</CODE>'s
constructor.</P>
<A NAME="auto1067"></A>
<PRE>
class PasteCommand : public Command {
public:
PasteCommand(Document*);
virtual void Execute();
private:
Document* _document;
};
PasteCommand::PasteCommand (Document* doc) {
_document = doc;
}
void PasteCommand::Execute () {
_document->Paste();
}
</PRE>
<A NAME="auto1068"></A>
<P>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 <CODE>SimpleCommand</CODE> for such
commands. <CODE>SimpleCommand</CODE> is parameterized by the
<CODE>Receiver</CODE> type and maintains a binding between a receiver object
and an action stored as a pointer to a member function.</P>
<A NAME="auto1069"></A>
<PRE>
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;
};
</PRE>
<A NAME="auto1070"></A>
<P>The constructor stores the receiver and the action in the corresponding
instance variables. <CODE>Execute</CODE> simply applies the action to the
receiver.</P>
<A NAME="auto1071"></A>
<PRE>
template <class Receiver>
void SimpleCommand<Receiver>::Execute () {
(_receiver->*_action)();
}
</PRE>
<A NAME="auto1072"></A>
<P>To create a command that calls <CODE>Action</CODE>
on an instance of class <CODE>MyClass</CODE>, a client simply writes</P>
<A NAME="auto1073"></A>
<PRE>
MyClass* receiver = new MyClass;
// ...
Command* aCommand =
new SimpleCommand<MyClass>(receiver, &MyClass::Action);
// ...
aCommand->Execute();
</PRE>
<A NAME="auto1074"></A>
<P>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 <CODE>Command</CODE> subclass.</P>
<A NAME="macrocommand2"></A>
<P>A <CODE>MacroCommand</CODE> 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.</P>
<A NAME="auto1075"></A>
<PRE>
class MacroCommand : public Command {
public:
MacroCommand();
virtual ~MacroCommand();
virtual void Add(Command*);
virtual void Remove(Command*);
virtual void Execute();
private:
List<Command*>* _cmds;
};
</PRE>
<A NAME="auto1076"></A>
<P>The key to the <CODE>MacroCommand</CODE> is its <CODE>Execute</CODE> member
function. This traverses all the subcommands and performs
<CODE>Execute</CODE> on each of them.</P>
<A NAME="auto1077"></A>
<PRE>
void MacroCommand::Execute () {
ListIterator<Command*> i(_cmds);
for (i.First(); !i.IsDone(); i.Next()) {
Command* c = i.CurrentItem();
c->Execute();
}
}
</PRE>
<A NAME="auto1078"></A>
<P>Note that should the <CODE>MacroCommand</CODE> implement an
<CODE>Unexecute</CODE> operation, then its subcommands must be
unexecuted in <EM>reverse</EM> order relative to <CODE>Execute</CODE>'s
implementation.</P>
<A NAME="auto1079"></A>
<P>Finally, <CODE>MacroCommand</CODE> must provide operations to manage its
subcommands. The <CODE>MacroCommand</CODE> is also responsible for
deleting its subcommands.</P>
<A NAME="auto1080"></A>
<PRE>
void MacroCommand::Add (Command* c) {
_cmds->Append(c);
}
void MacroCommand::Remove (Command* c) {
_cmds->Remove(c);
}
</PRE>
<A NAME="knownuses"><A>
<H2><A HREF="#relatedpatterns"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Known Uses</H2>
<A NAME="unidraw-use-comm"></A>
<P>Perhaps the first example of the Command pattern appears in a paper by
Lieberman [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=lieberman_menus" TARGET="_mainDisplayFrame">Lie85</A>]. MacApp [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=macapp" TARGET="_mainDisplayFrame">App89</A>] popularized
the notion of commands for implementing undoable operations.
ET++ [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=et++" TARGET="_mainDisplayFrame">WGM88</A>], InterViews [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>], and
Unidraw [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>] 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.</P>
<A NAME="think-use-comm"></A>
<P>The THINK class library [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=think" TARGET="_mainDisplayFrame">Sym93b</A>] also uses commands to support
undoable actions. Commands in THINK are called "Tasks." Task
objects are passed along a <A HREF="pat5afs.htm" TARGET="_mainDisplayFrame">Chain of Responsibility (223)</A>
for consumption.</P>
<A NAME="auto1081"></A>
<P>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.</P>
<A NAME="functor"></A>
<P>Coplien describes how to implement <STRONG>functors</STRONG>, objects that
are functions, in C++ [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=coplien_idioms" TARGET="_mainDisplayFrame">Cop92</A>]. He achieves a degree of
transparency in their use by overloading the function call operator
(<CODE>operator()</CODE>). The Command pattern is different; its focus
is on maintaining a <EM>binding between</EM> a receiver and a function
(i.e., action), not just maintaining a function.</P>
<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Related Patterns</H2>
<A NAME="auto1082"></A>
<P>A <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>
can be used to implement MacroCommands.</P>
<A NAME="auto1083"></A>
<P>A <A HREF="pat5efs.htm" TARGET="_mainDisplayFrame">Memento (283)</A>
can keep state the command requires to undo its effect.</P>
<A NAME="auto1084"></A>
<P>A command that must be copied before being placed on the history
list acts as a <A HREF="pat3dfs.htm"
TARGET="_mainDisplayFrame">Prototype (117)</A>.</P>
<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat5cfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat5cfs.htm"
TARGET="_mainDisplayFrame">Interpreter</A><BR>
<A HREF="pat5afs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat5afs.htm"
TARGET="_mainDisplayFrame">Chain of Responsibility</A>
</P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -