?? pat5f.htm
字號:
</OL><A NAME="implementation"></A><H2><A HREF="#samplecode"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Sample Code"></A> Implementation</H2> <A NAME="auto1048"></A><P>Here are two issues to consider when implementing the Memento pattern:</P><OL><A NAME="auto1049"></A><LI><EM>Language support.</EM>Mementos have two interfaces: a wide one for originators and a narrowone for other objects. Ideally the implementation language willsupport two levels of static protection. C++ lets you do this bymaking the Originator a friend of Memento and making Memento's wideinterface private. Only the narrow interface should be declaredpublic. For example:<A NAME="auto1050"></A><PRE> class State; class Originator { public: Memento* CreateMemento(); void SetMemento(const Memento*); // ... private: State* _state; // internal data structures // ... }; class Memento { public: // narrow public interface virtual ~Memento(); private: // private members accessible only to Originator friend class Originator; Memento(); void SetState(State*); State* GetState(); // ... private: State* _state; // ... };</PRE></LI><A NAME="state-incr-changes"></A><LI><EM>Storing incremental changes.</EM>When mementos get created and passed back to their originator in apredictable sequence, then Memento can save just the <EM>incrementalchange</EM> to the originator's internal state.<A NAME="undoable"></A><P>For example, undoable commands in a history list can use mementos toensure that commands are restored to their exact state when they'reundone (see <A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command (233)</A>). The history list defines aspecific order in which commands can be undone and redone. That meansmementos can store just the incremental change that a command makesrather than the full state of every object they affect. In theMotivation example given earlier, the constraint solver can store only thoseinternal structures that change to keep the line connecting therectangles, as opposed to storing the absolute positions of theseobjects.</P></LI></OL><A NAME="samplecode"><A><H2><A HREF="#knownuses"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Known Uses"></A> Sample Code</H2> <A NAME="auto1051"></A><P>The C++ code given here illustrates the ConstraintSolver example discussed earlier. Weuse MoveCommand objects (see <A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command (233)</A>) to (un)dothe translation of a graphical object from one position to another.The graphical editor calls the command's <CODE>Execute</CODE> operationto move a graphical object and <CODE>Unexecute</CODE> to undo the move.The command stores its target, the distance moved, and an instance of<CODE>ConstraintSolverMemento</CODE>, a memento containing state from theconstraint solver.</P><A NAME="auto1052"></A><PRE> class Graphic; // base class for graphical objects in the graphical editor class MoveCommand { public: MoveCommand(Graphic* target, const Point& delta); void Execute(); void Unexecute(); private: ConstraintSolverMemento* _state; Point _delta; Graphic* _target; };</PRE><A NAME="auto1053"></A><P>The connection constraints are established by the class<CODE>ConstraintSolver</CODE>. Its key member function is<CODE>Solve</CODE>, which solves the constraints registered withthe <CODE>AddConstraint</CODE> operation. To support undo,<CODE>ConstraintSolver</CODE>'s state can be externalized with<CODE>CreateMemento</CODE> into a <CODE>ConstraintSolverMemento</CODE>instance. The constraint solver can be returned to a previousstate by calling <CODE>SetMemento</CODE>. <CODE>ConstraintSolver</CODE>is a <A HREF="pat3efs.htm" TARGET="_mainDisplayFrame">Singleton (127)</A>.</P><A NAME="auto1054"></A><PRE> class ConstraintSolver { public: static ConstraintSolver* Instance(); void Solve(); void AddConstraint( Graphic* startConnection, Graphic* endConnection ); void RemoveConstraint( Graphic* startConnection, Graphic* endConnection ); ConstraintSolverMemento* CreateMemento(); void SetMemento(ConstraintSolverMemento*); private: // nontrivial state and operations for enforcing // connectivity semantics }; class ConstraintSolverMemento { public: virtual ~ConstraintSolverMemento(); private: friend class ConstraintSolver; ConstraintSolverMemento(); // private constraint solver state };</PRE><A NAME="auto1055"></A><P><A NAME="auto1056"></A><P>Given these interfaces, we can implement <CODE>MoveCommand</CODE> members<CODE>Execute</CODE> and <CODE>Unexecute</CODE> as follows:</P><A NAME="auto1057"></A><PRE> void MoveCommand::Execute () { ConstraintSolver* solver = ConstraintSolver::Instance(); _state = solver->CreateMemento(); // create a memento _target->Move(_delta); solver->Solve(); } void MoveCommand::Unexecute () { ConstraintSolver* solver = ConstraintSolver::Instance(); _target->Move(-_delta); solver->SetMemento(_state); // restore solver state solver->Solve(); }</PRE><A NAME="auto1058"></A><P><CODE>Execute</CODE> acquires a <CODE>ConstraintSolverMemento</CODE> mementobefore it moves the graphic. <CODE>Unexecute</CODE> moves the graphicback, sets the constraint solver's state to the previous state, andfinally tells the constraint solver to solve the constraints.</P><A NAME="knownuses"><A><H2><A HREF="#relatedpatterns"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Related Patterns"></A> Known Uses</H2> <A NAME="unidraw-use-meme"></A><P>The preceding sample code is based on Unidraw's support for connectivitythrough its CSolver class [<A HREF="bibfs.htm#unidraw_framework" TARGET="_mainDisplayFrame">VL90</A>].</P><A NAME="auto1059"></A><P>Collections in Dylan [<A HREF="bibfs.htm#Dylan" TARGET="_mainDisplayFrame">App92</A>] provide an iteration interface thatreflects the Memento pattern. Dylan's collections have the notion of a"state" object, which is a memento that represents the state of theiteration. Each collection can represent the current state of theiteration in any way it chooses; the representation is completelyhidden from clients. The Dylan iteration approach might be translatedto C++ as follows:</P><A NAME="auto1060"></A><PRE> template <class Item> class Collection { public: Collection(); IterationState* CreateInitialState(); void Next(IterationState*); bool IsDone(const IterationState*) const; Item CurrentItem(const IterationState*) const; IterationState* Copy(const IterationState*) const; void Append(const Item&); void Remove(const Item&); // ... };</PRE><A NAME="auto1061"></A><P><CODE>CreateInitialState</CODE> returns an initialized<CODE>IterationState</CODE> object for the collection. <CODE>Next</CODE> advancesthe state object to the next position in the iteration; it effectivelyincrements the iteration index. <CODE>IsDone</CODE> returns<CODE>true</CODE> if <CODE>Next</CODE> has advanced beyond the last elementin the collection. <CODE>CurrentItem</CODE> dereferences the stateobject and returns the element in the collection to which it refers.<CODE>Copy</CODE> returns a copy of the given state object. This isuseful for marking a point in an iteration.</P><A NAME="auto1062"></A><P>Given a class <CODE>ItemType</CODE>, we can iterate over a collection ofits instances as follows<A NAME="fn7"></A><SUP><A HREF="#footnote7">7</A></SUP>:</P><A NAME="auto1063"></A><PRE> class ItemType { public: void Process(); // ... }; Collection<ItemType*> aCollection; IterationState* state; state = aCollection.CreateInitialState(); while (!aCollection.IsDone(state)) { aCollection.CurrentItem(state)->Process(); aCollection.Next(state); } delete state;</PRE><A NAME="auto1064"></A><P>The memento-based iteration interface has two interesting benefits:</P><OL><A NAME="auto1065"></A><LI>More than one state can work on the same collection. (The sameis true of the <A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A> pattern.)</LI><A NAME="auto1066"></A><P></P><A NAME="auto1067"></A><LI>It doesn't require breaking a collection's encapsulationto support iteration. The memento is only interpreted by thecollection itself; no one else has access to it. Other approaches toiteration require breaking encapsulation by making iterator classesfriends of their collection classes (see<A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A>). The situation is reversed in thememento-based implementation: <CODE>Collection</CODE> is a friend of the<CODE>IteratorState</CODE>.</LI></OL><A NAME="qoca-use-memento"></A><P>The QOCA constraint-solving toolkit stores incremental information inmementos [<A HREF="bibfs.htm#qoca" TARGET="_mainDisplayFrame">HHMV92</A>]. Clients can obtain a memento that characterizesthe current solution to a system of constraints. The memento containsonly those constraint variables that have changed since the lastsolution. Usually only a small subset of the solver's variableschanges for each new solution. This subset is enough to return thesolver to the preceding solution; reverting to earlier solutionsrequires restoring mementos from the intervening solutions. Hence youcan't set mementos in any order; QOCA relies on a history mechanism torevert to earlier solutions.</P><A NAME="relatedpatterns"></A><H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: navigation"></A> Related Patterns</H2> <A NAME="auto1068"></A><P><A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command (233)</A>: Commands can use mementos to maintainstate for undoable operations.</P><A NAME="auto1069"></A><P><A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A>: Mementoscan be used for iteration as described earlier.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat5gfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat5gfs.htm" TARGET="_mainDisplayFrame">Observer</A><BR><A HREF="pat5efs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat5efs.htm" TARGET="_mainDisplayFrame">Mediator</A></P><HR><A NAME="footnote7"></A><SUP>7</SUP>Note that our example deletes thestate object at the end of the iteration. But <CODE>delete</CODE> won'tget called if <CODE>ProcessItem</CODE> throws an exception, thus creatinggarbage. This is a problem in C++ but not in Dylan, which has garbagecollection. We discuss a solution to this problem on<A HREF="pat5dfs.htm#clean-up_proxy_for_iterators" TARGET="_mainDisplayFrame">page 266</A>.</P></BODY></HTML>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -