Memento pattern

Last updated

The memento pattern is a software design pattern that exposes the private internal state of an object. One example of how this can be used is to restore an object to its previous state (undo via rollback), another is versioning, another is custom serialization.

Contents

The memento pattern is implemented with three objects: the originator, a caretaker and a memento. The originator is some object that has an internal state. The caretaker is going to do something to the originator, but wants to be able to undo the change. The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator. The memento object itself is an opaque object (one which the caretaker cannot, or should not, change). When using this pattern, care should be taken if the originator may change other objects or resources—the memento pattern operates on a single object.

Classic examples of the memento pattern include a pseudorandom number generator (each consumer of the PRNG serves as a caretaker who can initialize the PRNG (the originator) with the same seed (the memento) to produce an identical sequence of pseudorandom numbers) and the state in a finite state machine.

Overview

The Memento design pattern is one of the twenty-three well-known design patterns in the 1994 book Design Patterns by GoF that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse. The Memento Pattern was created by Noah Thompson, David Espiritu, and Dr. Drew Clinkenbeard for early HP products.

What problems can the Memento design pattern solve?

The problem is that a well designed object is encapsulated so that its representation (data structure) is hidden inside the object and can't be accessed from outside the object.

What solution does the Memento design pattern describe?

Make an object (originator) itself responsible for

Only the originator that created a memento is allowed to access it.

A client (caretaker) can request a memento from the originator (to save the internal state of the originator) and pass a memento back to the originator (to restore to a previous state).

This enables to save and restore the internal state of an originator without violating its encapsulation.

See also the UML class and sequence diagram below.

Structure

UML class and sequence diagram

A sample UML class and sequence diagram for the Memento design pattern. W3sDesign Memento Design Pattern UML.jpg
A sample UML class and sequence diagram for the Memento design pattern.

In the above UML class diagram, the Caretaker class refers to the Originator class for saving (createMemento()) and restoring (restore(memento)) originator's internal state.
The Originator class implements
(1) createMemento() by creating and returning a Memento object that stores originator's current internal state and
(2) restore(memento) by restoring state from the passed in Memento object.

The UML sequence diagram shows the run-time interactions:
(1) Saving originator's internal state: The Caretaker object calls createMemento() on the Originator object, which creates a Memento object, saves its current internal state (setState()), and returns the Memento to the Caretaker.
(2) Restoring originator's internal state: The Caretaker calls restore(memento) on the Originator object and specifies the Memento object that stores the state that should be restored. The Originator gets the state (getState()) from the Memento to set its own state.

Java example

The following Java program illustrates the "undo" usage of the memento pattern.

importjava.util.List;importjava.util.ArrayList;classOriginator{privateStringstate;// The class could also contain additional data that is not part of the// state saved in the memento..publicvoidset(Stringstate){this.state=state;System.out.println("Originator: Setting state to "+state);}publicMementosaveToMemento(){System.out.println("Originator: Saving to Memento.");returnnewMemento(this.state);}publicvoidrestoreFromMemento(Mementomemento){this.state=memento.getSavedState();System.out.println("Originator: State after restoring from Memento: "+state);}publicstaticclassMemento{privatefinalStringstate;publicMemento(StringstateToSave){state=stateToSave;}// accessible by outer class onlyprivateStringgetSavedState(){returnstate;}}}classCaretaker{publicstaticvoidmain(String[]args){List<Originator.Memento>savedStates=newArrayList<Originator.Memento>();Originatororiginator=newOriginator();originator.set("State1");originator.set("State2");savedStates.add(originator.saveToMemento());originator.set("State3");// We can request multiple mementos, and choose which one to roll back to.savedStates.add(originator.saveToMemento());originator.set("State4");originator.restoreFromMemento(savedStates.get(1));}}

The output is:

Originator: Setting state to State1 Originator: Setting state to State2 Originator: Saving to Memento. Originator: Setting state to State3 Originator: Saving to Memento. Originator: Setting state to State4 Originator: State after restoring from Memento: State3

This example uses a String as the state, which is an immutable object in Java. In real-life scenarios the state will almost always be a mutable object, in which case a copy of the state must be made.

It must be said that the implementation shown has a drawback: it declares an internal class. It would be better if this memento strategy could apply to more than one originator.

There are mainly three other ways to achieve Memento:

  1. Serialization.
  2. A class declared in the same package.
  3. The object can also be accessed via a proxy, which can achieve any save/restore operation on the object.

C# example

The memento pattern allows one to capture the internal state of an object without violating encapsulation such that later one can undo/revert the changes if required. Here one can see that the memento object is actually used to revert the changes made in the object.

classMemento{privatereadonlystringsavedState;privateMemento(stringstateToSave){savedState=stateToSave;}publicclassOriginator{privatestringstate;// The class could also contain additional data that is not part of the// state saved in the memento.publicvoidSet(stringstate){Console.WriteLine("Originator: Setting state to "+state);this.state=state;}publicMementoSaveToMemento(){Console.WriteLine("Originator: Saving to Memento.");returnnewMemento(state);}publicvoidRestoreFromMemento(Mementomemento){state=memento.savedState;Console.WriteLine("Originator: State after restoring from Memento: "+state);}}}classCaretaker{staticvoidMain(string[]args){varsavedStates=newList<Memento>();varoriginator=newMemento.Originator();originator.Set("State1");originator.Set("State2");savedStates.Add(originator.SaveToMemento());originator.Set("State3");// We can request multiple mementos, and choose which one to roll back to.savedStates.Add(originator.SaveToMemento());originator.Set("State4");originator.RestoreFromMemento(savedStates[1]);}}

Python example

"""Memento pattern example."""classOriginator:_state=""defset(self,state:str)->None:print(f"Originator: Setting state to {state}")self._state=statedefsave_to_memento(self)->"Memento":returnself.Memento(self._state)defrestore_from_memento(self,m:"Memento")->None:self._state=m.get_saved_state()print(f"Originator: State after restoring from Memento: {self._state}")classMemento:def__init__(self,state):self._state=statedefget_saved_state(self):returnself._statesaved_states=[]originator=Originator()originator.set("State1")originator.set("State2")saved_states.append(originator.save_to_memento())originator.set("State3")saved_states.append(originator.save_to_memento())originator.set("State4")originator.restore_from_memento(saved_states[1])

Javascript example

// The Memento pattern is used to save and restore the state of an object.// A memento is a snapshot of an object's state.varMemento={// Namespace: MementosavedState:null,// The saved state of the object.save:function(state){// Save the state of an object.this.savedState=state;},restore:function(){// Restore the state of an object.returnthis.savedState;}};// The Originator is the object that creates the memento.// defines a method for saving the state inside a memento.varOriginator={// Namespace: Originatorstate:null,// The state to be stored// Creates a new originator with an initial state of nullcreateMemento:function(){return{state:this.state// The state is copied to the memento.};},setMemento:function(memento){// Sets the state of the originator from a mementothis.state=memento.state;}};// The Caretaker stores mementos of the objects and// provides operations to retrieve them.varCaretaker={// Namespace: Caretakermementos:[],// The mementos of the objects.addMemento:function(memento){// Add a memento to the collection.this.mementos.push(memento);},getMemento:function(index){// Get a memento from the collection.returnthis.mementos[index];}};varaction_step="Foo";// The action to be executed/the object state to be stored.varaction_step_2="Bar";// The action to be executed/the object state to be stored.// set the initial stateOriginator.state=action_step;Caretaker.addMemento(Originator.createMemento());// save the state to the historyconsole.log("Initial State: "+Originator.state);// Foo// change the stateOriginator.state=action_step_2;Caretaker.addMemento(Originator.createMemento());// save the state to the historyconsole.log("State After Change: "+Originator.state);// Bar// restore the first state - undoOriginator.setMemento(Caretaker.getMemento(0));console.log("State After Undo: "+Originator.state);// Foo// restore the second state - redoOriginator.setMemento(Caretaker.getMemento(1));console.log("State After Redo: "+Originator.state);// Bar

Related Research Articles

A visitor pattern is a software design pattern that separates the algorithm from the object structure. Because of this separation new operations can be added to existing object structures without modifying the structures. It is one way to follow the open/closed principle in object-oriented programming and software engineering.

In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.

In object oriented programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.

In software design and engineering, the observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

In computer programming, the strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives runtime instructions as to which in a family of algorithms to use.

<span class="mw-page-title-main">Dependency injection</span> Software programming technique

In software engineering, dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally. Dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. The pattern ensures that an object or function that wants to use a given service should not have to know how to construct those services. Instead, the receiving 'client' is provided with its dependencies by external code, which it is not aware of. Dependency injection makes implicit dependencies explicit and helps solve the following problems:

Undo is an interaction technique which is implemented in many computer programs. It erases the last change done to the document, reverting it to an older state. In some more advanced programs, such as graphic processing, undo will negate the last command done to the file being edited. With the possibility of undo, users can explore and work without fear of making mistakes, because they can easily be undone.

<span class="mw-page-title-main">Multiton pattern</span> Software engineering design pattern

In software engineering, the multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map.

In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

In computer programming, variable shadowing occurs when a variable declared within a certain scope has the same name as a variable declared in an outer scope. At the level of identifiers, this is known as name masking. This outer variable is said to be shadowed by the inner variable, while the inner identifier is said to mask the outer identifier. This can lead to confusion, as it may be unclear which variable subsequent uses of the shadowed variable name refer to, which depends on the name resolution rules of the language.

Nemerle is a general-purpose, high-level, statically typed programming language designed for platforms using the Common Language Infrastructure (.NET/Mono). It offers functional, object-oriented, aspect-oriented, reflective and imperative features. It has a simple C#-like syntax and a powerful metaprogramming system.

In software engineering, the module pattern is a design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept.

<span class="mw-page-title-main">Umple</span> Object-oriented programming and modelling language

Umple is a language for both object-oriented programming and modelling with class diagrams and state diagrams. The name Umple is a portmanteau of "UML", "ample" and "Simple", indicating that it is designed to provide ample features to extend programming languages with UML capabilities.

In object-oriented programming, forwarding means that using a member of an object results in actually using the corresponding member of a different object: the use is forwarded to another object. Forwarding is used in a number of design patterns, where some members are forwarded to another object, while others are handled by the directly used object. The forwarding object is frequently called a wrapper object, and explicit forwarding members are called wrapper functions.

References

  1. "The Memento design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.