Chapter2 Memento
Chapter2 Memento
Contents
2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.6 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1
2.1 Introduction
This lecture note will introduce the Memento design pattern. In order to understand the
pattern, modelling delegation in UML will be discussed before the pattern is introduced.
C++ techniques required to implement the pattern will also be introduced before tackling
the pattern. This will support the reader to understand the example implementation of
this pattern.
Attributes and operations are assigned visibility. The visibility relates to whether the
feature (attribute or operation) is visible to the class that own the feature only, subclasses,
outside the class either the package or globally. The classifications for the visibility are
private, protected, package or public respectively. Private visibility is shown in UML
using a minus (-), protected a hash (#), package a tilde(˜), and public a plus (+) before
the feature. Omission of visibility means that it is either unknown or has not been shown.
Both attributes and operations can be further refined in order to be more descriptive.
Attribute refinements : There are three refinements that can be applied to attributes,
namely: default values; derived attributes and multiplicity. Figure 2 illustrates how
this are drawn in UML. dateRegistered is an example of an attribute with a default
value. age is an example of a derived attribute. The multiplicity of middleNames
states that an object of class Student may have 0 to 3 middle names representing
the state of the object. Multiplicity may further be refined showing whether the
values are ordered or unique, for example: addressLine[2..4]{ordered} indicates
that each object should have at least two and maximum 4 addressLines and that
the order in which these lines are used is important.
2
Figure 2: UML class attributes
Operation specification and refinement : Operations, refer to Figure 3, have the fol-
lowing form when specified in a class diagram: operationName(parameter list)
: return type. The parameter list is optional, but if included each param-
eter will have a basic form of: parameter name : parameter type, with the
parameter name being optional. Thus, setName(name : String) may also be
expressed as setName( : String). Omission of the return type assumes it to be
void.
Parameters may further be assigned default values. The form to assign default
values is by adding = default value to the basic form of a parameter. An example
would be assigning the current date to the date of registration for the operation for
registration approval, that is: registrationApproved(date : Date = today).
Each parameter can further be specified as in, out, or inout in order to distinguish
between pass-by-value and pass-by-reference in the implementation. The default is
pass by value and is therefore in.
3
2.2.2 Modelling delegation
Modelling delegation relationships between UML classes is achieved by drawing a solid
line between the classes involved in the relationship. Figure 4 shows two classes named
ClassA and ClassB that are associated with one another.
As with attributes, relationships also have multiplicity indicating the number of object
instances at the other end of the relationship for an instance of the current class. The
omission of multiplicity assumes 1. In Figure 5, the relationship shows that a library may
have many books, but that a book may belong to only one library.
Associated with multiplicity are role names and possibly their respective visibility. Fig-
ure 6 shows how this is achieved in UML. A student object will require to have exactly
one Address object for the relationship of home address.
Relationships, modelling delegation can be refined into two types of relations namely
dependencies and associations. Things such as naming a parameter type and creating an
object in a temporary variable imply a dependency. Associations are tighter relationships
than what dependencies are. An association is used to represent something like a field
in a class. Associations also come in two levels of granularity, namely: aggregation and
composition.
Each of these delegation-based relationships will be discussed further in the sections that
follow. Code that can serve as examples of how to implement the different relationship
types using C++ can be found in Section 2.3.2.
4
Dependency (uses-a relationship) indicates that there is a dependency between the two
classes in that one class makes use of the other class. Making use of a class could
either be as a parameter to an operation in the class or as a local variable in an
operation of the class. This relationship is weak and is shown in UML by an arrow
with a dotted line from the class that is using to the class that is being used. In the
dependency relationship shown in Figure 7, CrazyPrinter’s print operation accepts
a pointer to an object of DoubleWrapper as a parameter.
5
Figure 9: Composition association
6
The header file and corresponding implementation for the DoubleWrapper is given by:
DoubleWrapper.h
#i f n d e f DoubleWrapper H
#define DoubleWrapper H
c l a s s DoubleWrapper
{
public :
DoubleWrapper ( ) ;
DoubleWrapper ( double v ) ;
˜ DoubleWrapper ( ) ;
void s e t V a l u e ( double v ) ;
double g e t V a l u e ( ) ;
private :
double∗ v a l u e ;
};
#endif
DoubleWrapper.C
#include <i o s t r e a m >
#include ” DoubleWrapper . h”
using namespace s t d ;
DoubleWrapper : : DoubleWrapper ( ) : v a l u e ( 0 ) {}
double DoubleWrapper : : g e t V a l u e ( )
{
i f ( v a l u e != 0 )
{
return ∗ v a l u e ;
}
return −1;
}
7
DoubleWrapper : : ˜ DoubleWrapper ( )
{
i f ( v a l u e != 0 )
{
delete v a l u e ;
value = 0;
}
}
Note that value can be initialied in the initialiser list when the default constructor is
implemented. However this is not possible for the constructor taking an initial value as
parameter because the memory dynamic allocation needed to initialise this value can not
be done using the initialiser list. The initialiser list can only be used for heap allocation.
#include ” DoubleWrapper . h”
class CrazyPrinter
{
public :
CrazyPrinter ( ) ;
void p r i n t ( DoubleWrapper ∗ ) ;
};
#endif
CrazyPrinter.C
#include <i o s t r e a m >
#include ” C r a z y P r i n t e r . h”
void C r a z y P r i n t e r : : p r i n t ( DoubleWrapper ∗ v a l )
{
s t d : : cout<< val −>g e t V a l u e ( ) << s t d : : e n d l ;
}
8
Aggregation association relationship is a relationship in which the class APrinter
has a handle to an object of DoubleWrapper as can be seen in Figure 8. The handle
is a pointer to an instance of the wrapper object in heap memory.
APrinter.h
#i f n d e f APrinter H
#define APrinter H
#include ” DoubleWrapper . h”
c l a s s A Pr i nt e r
{
public :
A Pr i nt e r ( DoubleWrapper ∗ ) ;
˜ AP r in t er ( ) ;
void p r i n t ( ) ;
void update ( DoubleWrapper ∗ ) ;
protected :
A Pr i nt e r ( ) ;
private :
DoubleWrapper ∗ doubleValue ;
};
#endif
APrinter.C
#include <i o s t r e a m >
#include ” AP r in t er . h”
using namespace s t d ;
A P r i nt e r : : A P ri n te r ( DoubleWrapper ∗ v a l u e ) : doubleValue ( v a l u e ) {}
void A Pr i nt e r : : p r i n t ( )
{
i f ( doubleValue != 0 )
{
cout << doubleValue−>g e t V a l u e ( ) << e n d l ;
}
else
{
cout << ” u n d e f i n e d ” << e n d l ;
}
}
9
A P r i nt e r : : ˜ A Pr i n te r ( )
{
doubleValue = 0 ;
}
RobustDoubleWrapper.h
#i f n d e f RobustDoubleWrapper H
#define RobustDoubleWrapper H
c l a s s RobustDoubleWrapper
{
public :
RobustDoubleWrapper ( ) ;
// copy c o n s t r u c t o r added
RobustDoubleWrapper ( const RobustDoubleWrapper &);
RobustDoubleWrapper ( double ) ;
˜ RobustDoubleWrapper ( ) ;
// a s s i g n m e n t o p e r a t o r added
RobustDoubleWrapper& operator = ( const RobustDoubleWrapper &);
void s e t V a l u e ( double ) ;
double g e t V a l u e ( ) ;
private :
double∗ v a l u e ;
};
#endif
RobustDoubleWrapper.C
#include <i o s t r e a m >
#include ” RobustDoubleWrapper . h”
using namespace s t d ;
RobustDoubleWrapper : : RobustDoubleWrapper ( ) : v a l u e ( 0 ) { }
10
RobustDoubleWrapper : :
RobustDoubleWrapper ( const RobustDoubleWrapper& c )
{
i f ( v a l u e != 0 )
{
delete v a l u e ;
}
v a l u e = new double ( ∗ ( c . v a l u e ) ) ;
}
RobustDoubleWrapper : : ˜ RobustDoubleWrapper ( )
{
i f ( v a l u e != 0 )
{
delete v a l u e ;
value = 0;
}
}
RobustDoubleWrapper& RobustDoubleWrapper : :
operator = ( const RobustDoubleWrapper& s )
{
i f ( v a l u e != 0 )
{
delete v a l u e ;
}
v a l u e = new double ( ∗ ( s . v a l u e ) ) ;
return ∗ t h i s ;
}
11
double RobustDoubleWrapper : : g e t V a l u e ( )
{
i f ( v a l u e != 0 )
{
return ∗ v a l u e ;
}
return −1;
}
AnotherPrinter.h
#i f n d e f A n o t h e r P r i n t e r H
#define A n o t h e r P r i n t e r H
#include ” RobustDoubleWrapper . h”
class AnotherPrinter
{
public :
A n o t h e r P r i n t e r ( RobustDoubleWrapper ∗ doubleValue ) ;
˜ AnotherPrinter ( ) ;
void p r i n t ( ) ;
void update ( RobustDoubleWrapper ∗ doubleValue ) ;
protected :
AnotherPrinter ( ) ;
private :
RobustDoubleWrapper doubleValue ;
};
#endif
AnotherPrinter.C
/∗
Note : t h e commented code d e s c r i b e t h e c h a n g e s
r e q u i r e d compared t o t h e c l a s s APrinter
∗/
#include ” A n o t h e r P r i n t e r . h”
using namespace s t d ;
A n o t h e r P r i n t e r : : A n o t h e r P r i n t e r ( RobustDoubleWrapper ∗ v a l u e )
{
// cannot use i n i t i a l i s e r l i s t owing t o d e r e f e r n c i n g needed .
doubleValue = ∗ v a l u e ;
}
12
void A n o t h e r P r i n t e r : : p r i n t ( )
{
// no c o n d i t i o n needed −− d o u b l e V a l u e i s on t h e s t a c k
// a l s o n o t e t h e use o f . i n s t e a d o f −> t o c a l l t h e f u n c t i o n
cout << doubleValue . g e t V a l u e ( ) << e n d l ;
}
A n o t h e r P r i n t e r : : ˜ A n o t h e r P r i n t e r ( ) {}
// d o u b l e V a l u e i s on t h e s t a c k and i s a u t o m a t i c a l l y r e l e a s e d .
Friends in C++
In order for another class to be able to access features in a given class that are not
public, the class must be assigned “friend”-status. Friend status can be protected or
private. The visibility of the “friend”-status specifies to which category of members
of the class the friend will be granted access. Refer to the example in Section 2.6 to
see how the Originator is given private friend status of the Memento class to widen
the interface of the Memento class with the Originator class while maintaining a
narrow interface of the Memento class with the Caretaker class.
2.4.1 Identification
Name Classification Strategy
Memento Behavioural Delegation (Object)
Intent
“Without violating encapsulation, capture and externalise an object’s internal
state so that the object can be restored to this state later.” ([2]:283)
13
2.4.2 Structure
2.4.3 Problem
The memento pattern enables an object to be restored to its previous state. Memento
can be seen as a snapshot of the system at a particular point in time.
2.4.4 Participants
Originator
Memento
Caretaker
14
The two main participants on the pattern are the Originator and the Caretaker. A wide
interface exists between the Originator and the Memento, while a narrow interface exists
between the Caretaker and the Memento.
2.5.1 Clarification
The memento pattern is handy when there is the need to keep information in tact for use
at a later stage. The pattern is only useful when the time taken to store and later restore
the state does not impact heavily on the functionality and performance of the system
being developed.
Iterator
Mementos can be used for iteration to maintain the state of the iterator.
Bridge
The bridge pattern can be applied in order to separate the interface from the imple-
mentation of the memento in order to provide the wide interface between the orig-
inator and the memento without using the friend technique which violates object-
oriented encapsulation.
2.6 Example
Consider a calculator application for complex numbers. As all calculators have the func-
tionality to store and recall number from memory so must this implementation of the
complex calculator. The code presented shows the essence of such an implementation and
can be extended in order to provide all operations and required calculator functionality.
The UML class diagram for the application is given in Figure 11. Interesting aspects of
the implementation, and in particular the implementation for the friend relationship is
shown here.
In this example we give inline implementations of all definitions and assume that all
code is written in a single .cpp file. When doing this the order in which the classes
are presented is important. One class cannot use another class before it is declared. In
15
this case ComplexNumber requires StoredComplexNumber to be defined before itself and
vice versa. In such a case one determine which of the two requires the most detail of
the other to be exposed and declare the that requires the least information of the other
first. As can be seen from the implementation of the class , it does not need much detail
regarding the ComplexNumber class. It basically needs to know that it exists. Therefore
class StoredComplexNumber can be defined before class ComplexNumber as long as the
line class ComplexNumber; (a forward declaration similar to a function prototype) is
inserted just before where class StoredComplexNumber is defined.
Figure 11: Class diagram for an implementation of a complex number calculator that
illustrates the Memento design pattern.
16
s t a t e = new S t a t e ( val1 , v a l 2 ) ;
}
State ∗ state ;
};
ComplexNumber excerpts
ComplexNumber : : ComplexNumber ( ) : real (0) , i m a g i n a r y ( 0 ) {}
StoredComplexNumber ∗ retreiveMemento ( )
{
return mem ;
};
˜ Store ()
{
delete mem ;
};
private :
StoredComplexNumber ∗ mem ;
};
The design includes the implementation of the State class. This class encapsulates all the
instance variables of the ComplexNumber class. It is used by the StoredComplexNumber
17
class. While it is not required that the complete state of the originator be stored in a
single object, it is recommended because it may simplify the combination of the memento
pattern with other patterns.
2.7 Exercises
1. Change the example given for composition so that the object in AnotherPrinter is
not on the stack but on the heap without changing the relationship to aggregation
as in the APrinter example.
References
[1] Simon Bennett, John Skelton, and Ken Lunn. Schaum’s Outline of UML. McGraw-Hill
Professional, UK, 2001. ISBN 0077096738.
[2] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design patterns :
elements of reusable object-oriented software. Addison-Wesley, Reading, Mass, 1995.
18