Memento is a behavioral design pattern which can be used to externalize the state of an object to provide rollback functionality. This tutorial explains this pattern with Java code examples.
What is Memento Pattern?
Memento pattern allows you to save and restore the previous state of an object without exposing the details of its implementation. Let’s suppose we have a payroll desktop application and we want the employees to be able to edit their employee information including name, address and phone number for now. Eventually, we decide to offer undo option. In order to perform undo, we have to save the state of the objects somewhere. The first option would be to create a Stack of Employee object. Everytime, the user saves their state or periodically the state gets stored in the stack. When the user wants to revert, we can pop the state from the Stack and apply them to current Employee object. There are several problems with this approach.
- In order to save the state, we have to iterate through all fields of the object we want to save and store their values in another copy. For this we need access to all fields of the
Employeeobject. This can work only ifEmployeehad those fields available to other objects usingpublicaccess modifier. In real-life objects, it may not allow others to look into those internal details and may be hiding those details in private fields. - If we relax this requirement and make all fields public or accessible for other object, we can copy the state easily. However, if you modify the available fields of the original
Employeeobject, we have to modify the object which is responsible for copying the state of all the originalEmployeeobject.
The problem is we are trying to externalize the state saving to other object. Instead, if we add this logic to the object itself, it can access its private fields and we can have logic to store whatever we want to store. The Memento pattern delegates the state snapshot creation to the actual owner of the state. This pattern recommends storing the copy of object’s state in an object known as memento. The data from the memento is not accessible to any other object except the original producer. Other objects communicate memento objects using only getters, but does not allow getting the state of the original object. We also have caretaker objects which stores memento objects. Again, this does not allow caretaker to modify the contents of the memento object. This also allows us to store only required fields as memento can have only subset of fields from original object. This pattern is useful whne you have objects whose behavior depend on their current state.
How to implement Memento Pattern?
The Memento pattern consists of following main actors:
- Originator: This is the class whose snapshots we want to capture and this will be the class that can produce the snapshots. This class with have methods for taking snapshot
save()andrestore()to revert the state. It works with memento objects. - Memento: This is an object which stores the state of
Originator. Most likely this will be immutable, that is it will not have any setter method defined in it. - Caretaker: This is an object which stores the memento. It also hides implementation of how the data is stored. It only exposes methods on how to save the memento and how to revert the previous state. Usually
Stackis used to store memento objects in caretaker.
Practical Code - Payroll App
- We have an object that we want to store state of. In this case, you have Payroll app and you want to store the state of
Employeeobject.
1public class Employee {
2 private String employeeId;
3 private String name;
4 private String address;
5 private String phone;
6
7 public Employee(String employeeId, String name, String address, String phone) {
8 this.employeeId = employeeId;
9 this.name = name;
10 this.address = address;
11 this.phone = phone;
12 }
13
14 public Employee() {}
15
16 public String getEmployeeId() {
17 return employeeId;
18 }
19
20 public void setEmployeeId(String employeeId) {
21 this.employeeId = employeeId;
22 }
23
24 public String getName() {
25 return name;
26 }
27
28 public void setName(String name) {
29 this.name = name;
30 }
31
32 public String getAddress() {
33 return address;
34 }
35
36 public void setAddress(String address) {
37 this.address = address;
38 }
39
40 public String getPhone() {
41 return phone;
42 }
43
44 public void setPhone(String phone) {
45 this.phone = phone;
46 }
47
48 public EmployeeMemento save() {
49 return new EmployeeMemento(employeeId, name, phone);
50 }
51
52 public void revert(EmployeeMemento employee) {
53 this.name = employee.getName();
54 this.address = employee.getAddress();
55 this.phone = employee.getPhone();
56 }
57
58 @Override
59 public String toString() {
60 return "Employeed ID: " + employeeId + " with name: " + name + " residing at " + address + " has phone number: " + phone;
61 }
62
63}
- Create
EmployeeMementocontaining only information that we want to store from originalEmployee. In this case, the app will not allow employee to change theemployeeIdso we are not going to store that data into our memento. Also, note that memento only provides getters such that we cannot modify the stored state.
1public class EmployeeMemento {
2 private String name;
3 private String address;
4 private String phone;
5
6 public EmployeeMemento(String name, String address, String phone) {
7 this.name = name;
8 this.address = address;
9 this.phone = phone;
10 }
11
12 public String getAddress() {
13 return address;
14 }
15
16 public String getName() {
17 return name;
18 }
19
20 public String getPhone() {
21 return phone;
22 }
23
24 public String toString() {
25 return name + " residing at " + address + " has phone number: " + phone;
26 }
27}
- Next define the
Caretakerobject which will store the history.
1public class EmployeeCaretaker {
2 private Stack<EmployeeMemento> history = new Stack<>();
3
4 public void save(Employee employee) {
5 history.push(employee.save());
6 }
7
8 public void revert(Employee employee) {
9 employee.revert(history.pop());
10 }
11}
- Now the client works with
Caretakerobject to save and rollback the history.
1public class ClientMain {
2 public static void main(String[] args) {
3
4 EmployeeCaretaker caretaker = new EmployeeCaretaker();
5
6 Employee john = new Employee();
7
8 john.setEmployeeId("123");
9 john.setName("John Doe");
10 john.setAddress("101 King Street");
11 john.setPhone("999-555-6666");
12
13 System.out.println("Saving Employee: " + john);
14 caretaker.save(john);
15
16 john.setPhone("444-555-6666");
17 caretaker.save(john);
18
19 System.out.println("Employee Details: " + john);
20 john.setPhone("333-999-6666");
21 // When we call save, it will save the current state and next revert will revert to this state
22// caretaker.save(john);
23 caretaker.revert(john);
24
25 System.out.println("Employee Details: " + john);
26 caretaker.revert(john);
27 System.out.println("Employee Detail: " + john);
28 }
29}
Advantages:
- This is great pattern when we want to have rollback feature.
- It encapsulates originator and allows to hide internal members.
Disadvantages:
- This pattern can be expensive in memory if we have to store large copy of originator data.
- It requires careful implementation to not expose information of memento objects.
- Caretaker object needs to handle how much history it should keep and how to delete older states.
Summary
- When we want to implement undo/redo kind of functionality, Memento pattern is very suitable one.
- This pattern can be used to create snapshots of the originator object.
- It looks similar to command pattern with history and state.
- You need to be careful with caretaker to not store infinite history.


Comments