Behavioral

Memento (Token)

Purpose

Allows storing a snapshot of the internal state of an object (originator) into another object (memento), so that the originator can be restored to its original state, if required.

UML

Behavior

Before an originator changes it state, a caretaker requests a memento object from it. The originator can then restore its original state (if required) using the memento object held by the caretaker.

The memento object protects against access by any object other than its originator. Mementos effectively have two interfaces:

  1. A narrow interface seen by the caretaker: The caretaker can only pass the memento to other objects.
  2. A wide interface seen by the originator: The originator can access everything in the memento to restore itself to its previous state.

These two interfaces are obtained by making all interface methods in the memento private (item 1), and making the originator a friend of the memento (item 2).

Example

An application may allow a user to change and then undo (if required) their changes to a specific object, say user preferences. In this example, the Memento pattern provides the undo ability by requesting a CUserPreferencesMemento object from CUserPreferences object before any user-changes are applied to it. The user may undo their preferences any time later on by using CUserPreferencesMemento to reset CUserPreferences to its initial state.

Usage

/* Originator */

/* Originator: Creates a memento object containing a snapshot of its internal state. It also uses the memento object to restore its internal state when needed */
class CUserPreferences
{
// Constructors/Destructor
public:
    CUserPreferences( const int n, const long l, const std::string s) : 
        nData1(n), lData2(l), strData3(s) {}

// Public Interface
public:
    CUserPreferencesMemento* CreateMemento();
    void SetMemento( const std::auto_ptr<CUserPreferencesMemento>& );

    void Set_nData1( const int n)             { nData1 = n; }
    void Set_lData2( const long l)            { lData2 = l; }
    void Set_strData3( const std::string str) { strData3 = str; }

// Data members
private:
    // State of the object
    int nData1;
    long lData2;
    std::string strData3;
};

/* Create a memento and initialize it with originate state */
CUserPreferencesMemento* CUserPreferences::CreateMemento()
{
    // Create a new memento ...
    CUserPreferencesMemento* pMem = new CUserPreferencesMemento();

    // and initialize its state with that of the originator
    pMem->SetState( nData1, lData2, strData3);

    return pMem;
}

/* Use the memento object to reset originator to its state. Note that passing a const auto_ptr<>& implies that ownership cannot be transferred! */
void CUserPreferences::SetMemento( const std::auto_ptr<CUserPreferencesMemento>& spMem)
{
    spMem->GetState(nData1, lData2, strData3);
}

/* Memento */

/* Memento: Stores the internal state of its originator. Note how it effectively supports both narrow and wide interfaces */
class CUserPreferencesMemento
{
// Constructors/Destructor
public:
    ~CUserPreferencesMemento(){}
private:
    // private constructor. No one can create this object except for the originator, 
    // CUserPreferences.
    CUserPreferencesMemento() {}

// private interface
private:
    // No class can access this interface except for friends
    friend class CUserPreferences;
    void SetState( int n, long l, std::string& s)
    {
        m_nData1 = n; m_lData2 = l; m_strData3 = s;
    }
    void GetState(int& n, long& l, std::string& s) const
    {
        n = m_nData1 ; l = m_lData2 ; s = m_strData3;
    }

// Data members
private:
    // State of the originator.
    int m_nData1;
    long m_lData2;
    std::string m_strData3;
};

int main(int argc, char* argv[])
{
    // Create and initialize an originator object 
    CUserPreferences obPref( 1,2,"c:\\projects"); 

    // Initialize a memento with the originator state before changing its state
    std::auto_ptr<CUserPreferencesMemento> spMem( obPref.CreateMemento() );

    // Now change the state of the originator
    obPref.Set_nData1( 10 );
    obPref.Set_lData2( 20 );

    // Discard earlier state changes and restore originator from its memento. Note
    // that spMem is passed as a const& thereby bypassing ownership transfer.
    obPref.SetMemento( spMem );

    return 0; // spMem deleted here!
}

Notes