📄 pat5f.htm
字号:
<HTML>
<HEAD><TITLE>Memento</TITLE></HEAD>
<BODY BGCOLOR = #FFFFFF
>
<A NAME="top"></A>
<A NAME="Memento"></A>
<A NAME="intent"></A>
<H2><A HREF="#alsoknownas"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Intent</H2>
<A NAME="auto1000"></A>
<P>Without violating encapsulation, capture and externalize an object's
internal state so that the object can be restored to this state later.</P>
<A NAME="alsoknownas"><A>
<H2><A HREF="#motivation"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Also Known As</H2>
<A NAME="auto1001"></A>
<P>Token</P>
<A NAME="motivation"></A>
<H2><A HREF="#applicability"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Motivation</H2>
<A NAME="auto1002"></A>
<P>Sometimes it's necessary to record the internal state of an object.
This is required when implementing checkpoints and undo mechanisms
that let users back out of tentative operations or recover from
errors. You must save state information somewhere so that you can
restore objects to their previous states. But objects normally
encapsulate some or all of their state, making it inaccessible to
other objects and impossible to save externally. Exposing this state
would violate encapsulation, which can compromise the application's
reliability and extensibility.</P>
<A NAME="auto1003"></A>
<P>Consider for example a graphical editor that supports connectivity
between objects. A user can connect two rectangles with a line, and
the rectangles stay connected when the user moves either of them. The
editor ensures that the line stretches to maintain the connection.</P>
<P ALIGN=CENTER><IMG SRC="Pictures/memen029.gif"></P>
<A NAME="auto1004"></A>
<P>A well-known way to maintain connectivity relationships between
objects is with a constraint-solving system. We can encapsulate this
functionality in a <STRONG>ConstraintSolver</STRONG> object.
ConstraintSolver records connections as they are made and generates
mathematical equations that describe them. It solves these equations
whenever the user makes a connection or otherwise modifies the
diagram. ConstraintSolver uses the results of its calculations to
rearrange the graphics so that they maintain the proper connections.</P>
<A NAME="supp-undo"></A>
<P>Supporting undo in this application isn't as easy as it may seem. An
obvious way to undo a move operation is to store the original distance
moved and move the object back an equivalent distance. However, this
does not guarantee all objects will appear where they did before.
Suppose there is some slack in the connection. In that case, simply
moving the rectangle back to its original location won't necessarily
achieve the desired effect.</P>
<P ALIGN=CENTER><IMG SRC="Pictures/memen028.gif"></P>
<A NAME="auto1005"></A>
<P>In general, the ConstraintSolver's public interface might be
insufficient to allow precise reversal of its effects on other
objects. The undo mechanism must work more closely with
ConstraintSolver to reestablish previous state, but we should also
avoid exposing the ConstraintSolver's internals to the undo mechanism.</P>
<A NAME="orig-def"></A>
<P>We can solve this problem with the Memento pattern. A
<STRONG>memento</STRONG> is an object that stores a snapshot of the
internal state of another object—the memento's
<STRONG>originator</STRONG>. The undo mechanism will request a memento
from the originator when it needs to checkpoint the originator's
state. The originator initializes the memento with information that
characterizes its current state. Only the originator can store and
retrieve information from the memento—the memento is "opaque" to
other objects.</P>
<A NAME="auto1006"></A>
<P>In the graphical editor example just discussed, the ConstraintSolver can act
as an originator. The following sequence of events characterizes the
undo process:</P>
<OL>
<A NAME="auto1007"></A>
<LI>The editor requests a memento from the ConstraintSolver as a
side-effect of the move operation.</LI>
<A NAME="auto1008"></A>
<P></P>
<A NAME="solverstate"></A>
<LI>The ConstraintSolver creates and returns a memento, an instance of a
class SolverState in this case. A SolverState memento contains data
structures that describe the current state of the ConstraintSolver's
internal equations and variables.</LI>
<A NAME="auto1009"></A>
<P></P>
<A NAME="auto1010"></A>
<LI>Later when the user undoes the move operation, the editor gives the
SolverState back to the ConstraintSolver.</LI>
<A NAME="auto1011"></A>
<P></P>
<A NAME="auto1012"></A>
<LI>Based on the information in the SolverState, the ConstraintSolver
changes its internal structures to return its equations and variables
to their exact previous state.</LI>
</OL>
<A NAME="auto1013"></A>
<P>This arrangement lets the ConstraintSolver entrust other objects with
the information it needs to revert to a previous state without
exposing its internal structure and representations.</P>
<A NAME="applicability"></A>
<H2><A HREF="#structure"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Applicability</H2>
<A NAME="auto1014"></A>
<P>Use the Memento pattern when</P>
<UL>
<A NAME="auto1015"></A>
<LI>a snapshot of (some portion of) an object's state must be saved so
that it can be restored to that state later, <EM>and</EM></LI>
<A NAME="auto1016"></A>
<P></P>
<A NAME="auto1017"></A>
<LI>a direct interface to obtaining the state would expose
implementation details and break the object's encapsulation.</LI>
</UL>
<A NAME="structure"></A>
<H2><A HREF="#participants"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Structure</H2>
<A NAME="orig-285c"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/memento.gif"></P>
<A NAME="participants"></A>
<H2><A HREF="#collaborations"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Participants</H2>
<UL>
<A NAME="auto1018"></A>
<LI><B>Memento</B> (SolverState)</LI>
<A NAME="auto1019"></A>
<P></P>
<UL>
<A NAME="auto1020"></A>
<LI>stores internal state of the Originator object. The memento may
store as much or as little of the originator's internal state as
necessary at its originator's discretion.</LI>
<A NAME="auto1021"></A>
<P><!-- extra space --></P>
<A NAME="auto1022"></A>
<LI>protects against access by objects other than the
originator. Mementos have effectively two interfaces. Caretaker
sees a <EM>narrow</EM> interface to the Memento—it can only pass
the memento to other objects. Originator, in contrast, sees a
<EM>wide</EM> interface, one that lets it access all the data
necessary to restore itself to its previous state. Ideally, only
the originator that produced the memento would be permitted to
access the memento's internal state.</LI>
</UL>
<A NAME="auto1023"></A>
<P></P>
<A NAME="auto1024"></A>
<LI><B>Originator</B> (ConstraintSolver)</LI>
<A NAME="auto1025"></A>
<P></P>
<UL>
<A NAME="auto1026"></A>
<LI>creates a memento containing a snapshot of its current
internal state.</LI>
<A NAME="auto1027"></A>
<P><!-- extra space --></P>
<A NAME="auto1028"></A>
<LI>uses the memento to restore its internal state.</LI>
</UL>
<A NAME="auto1029"></A>
<P></P>
<A NAME="auto1030"></A>
<LI><B>Caretaker</B> (undo mechanism)
<A NAME="auto1031"></A>
<P></P>
<UL>
<A NAME="auto1032"></A>
<LI>is responsible for the memento's safekeeping.</LI>
<A NAME="auto1033"></A>
<P><!-- extra space --></P>
<A NAME="auto1034"></A>
<LI>never operates on or examines the contents of a memento.</LI>
</UL>
</UL>
<A NAME="collaborations"></A>
<H2><A HREF="#consequences"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Collaborations</H2>
<UL>
<A NAME="auto1035"></A>
<LI>A caretaker requests a memento from an originator, holds it for a
time, and passes it back to the originator, as the following
interaction diagram illustrates:
<A NAME="orig-286i"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/memen027.gif"></P>
<A NAME="auto1036"></A>
<P>Sometimes the caretaker won't pass the memento back to the originator,
because the originator might never need to revert to an earlier state.</P>
</LI>
<A NAME="auto1037"></A>
<P></P>
<A NAME="auto1038"></A>
<LI>Mementos are passive. Only the originator that created a memento will
assign or retrieve its state.</LI>
</UL>
<A NAME="consequences"></A>
<H2><A HREF="#implementation"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Consequences</H2>
<A NAME="auto1039"></A>
<P>The Memento pattern has several consequences:</P>
<OL>
<A NAME="presrvencap"></A>
<LI><EM>Preserving encapsulation boundaries.</EM>
Memento avoids exposing information that only an originator should
manage but that must be stored nevertheless outside the originator.
The pattern shields other objects from potentially complex Originator
internals, thereby preserving encapsulation boundaries.</LI>
<A NAME="auto1040"></A>
<P></P>
<A NAME="auto1041"></A>
<LI><EM>It simplifies Originator.</EM>
In other encapsulation-preserving designs, Originator keeps the
versions of internal state that clients have requested. That puts all
the storage management burden on Originator. Having clients
manage the state they ask for simplifies Originator and keeps
clients from having to notify originators when they're done.</LI>
<A NAME="auto1042"></A>
<P></P>
<A NAME="auto1043"></A>
<LI><EM>Using mementos might be expensive.</EM>
Mementos might incur considerable overhead if Originator must copy
large amounts of information to store in the memento or if clients
create and return mementos to the originator often enough. Unless
encapsulating and restoring Originator state is cheap, the pattern
might not be appropriate. See the discussion of incrementality in the
Implementation section.</LI>
<A NAME="auto1044"></A>
<P></P>
<A NAME="auto1045"></A>
<LI><EM>Defining narrow and wide interfaces.</EM>
It may be difficult in some languages to ensure that only the
originator can access the memento's state.</LI>
<A NAME="auto1046"></A>
<P></P>
<A NAME="auto1047"></A>
<LI><EM>Hidden costs in caring for mementos.</EM>
A caretaker is responsible for deleting the mementos it cares for.
However, the caretaker has no idea how much state is in the memento.
Hence an otherwise lightweight caretaker might incur large storage
costs when it stores mementos.</LI>
</OL>
<A NAME="implementation"></A>
<H2><A HREF="#samplecode"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Implementation</H2>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -