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.

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.

In object-oriented programming, the factory method pattern is a design pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact classes. Rather than by calling a constructor, this is accomplished by invoking a factory method to create an object. Factory methods can be specified in an interface and implemented by subclasses or implemented in a base class and optionally overridden by subclasses. It is one of the 23 classic design patterns described in the book Design Patterns and is subcategorized as a creational pattern.

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 object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.

In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.

In computer programming, a callback is a function that is stored as data and designed to be called by another function – often back to the original abstraction layer.

In computing based on the Java Platform, JavaBeans is a technology developed by Sun Microsystems and released in 1996, as part of JDK 1.1.

<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:

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 science, recursive ascent parsing is a technique for implementing an LR parser which uses mutually-recursive functions rather than tables. Thus, the parser is directly encoded in the host language similar to recursive descent. Direct encoding usually yields a parser which is faster than its table-driven equivalent for the same reason that compilation is faster than interpretation. It is also (nominally) possible to hand edit a recursive ascent parser, whereas a tabular implementation is nigh unreadable to the average human.

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.

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.

JExcel is a library (API) to read, write, display, and modify Excel files with .xls or .xlsx formats. API can be embedded with Java Swing and AWT.

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.