In this post I will show you a memento pattern on one very simple example. I will not use a real world example from a real job because my real world problem was mixed with visitor and state pattern. If you want to see that problem that is a mixture of a visitor, state and memento and how to solve it, check this article

Idea of the pattern

The concept behind the pattern is to retain the previous states of an object, allowing the recreation of those states in the future. We aim to provide the flexibility to request specific states, such as “Retrieve the state that was valid on 4.7.2020” or “Retrieve the state after the last modification made by John Doe.” To achieve this, it is necessary to store each historical state of the person in the database, along with additional information about the person responsible for introducing the change and the time when the change was introduced.

First attempt

Consider a scenario where we have a class named Person. I will make that class very simple(for the sake of example) but you can imagine that Person has a rich domain knowledge:

public class Person {
    private String id;
    private String name;
    private int salary;
    public void promote() {
        salary = salary * 2;//if someone works on HR software, please implement this      feature :)
    }
}

We can create a person named Frenk, set an initial salary of 80000, and then invoke the promote function, resulting in a salary of 160000. Both states of the object must be stored in a History class(so we can recreate that state later). However, a challenge arises as making a copy (snapshot) of the person object is necessary for saving it to the snapshot history.

The issue lies in the fact that within the History class, creating a copy of the Person class is impossible due to encapsulation, as not all fields are accessible outside the Person class.

An alternative approach involves introducing a List<Person> field within the Person class to store the history. This method would require implementing functions such as getStateOnDay(LocalDate date) and getLastStateMadeBy(Person p).

public class Person {
    private String id;
    private String name;
    private int salary;
    private List<Person> history;
    public Person getStateOnDay(LocalDate date) {
        //return state on a particular date
    }

    public Person getLastStateMadeBy(Person p) {
        
//return the state after last change by person p
    }
    //rest of the code which contains rich business model
}

This seems like an “ok” solution

It’s not that easy(at least not for more junior developers) to spot why this approach is not a good idea. Many people will think that this solution is not ideal but it’s not a big deal.

First of all, if a Person class is mutable a compilcation arises. This is problematic as all entries within the history list become mutable, and it is undesirable to permit changes to records already stored in the history!

person.getStateOnDay(LocalDate.of(2020, 5, 13)).setName("Valerie");
//mutating history data which we don't want to allow!

For sure, when returning history records to the outside world, we can make defensive copies or something similar and that will help but still someone inside the class can compromise immutability of the history records.


An additional challenge arises when attempting to store the Person object in one database and its history in another. While this scenario is uncommon, the core issue lies in the coupling between the Person and its history. Any situation demanding separate actions on the Person class and its history introduces complications and potential problems.

Also, inside class Person we have rich business domain(you remember that on beggining we imagined that person is rich business class) but also this class takes care of the history changes and it’s own history. That really smells like single responsibility principle violated!

Dividing business responsibilities from history responsibilities

We aim to relocate all aspects related to history out of the Person class. However, only inside the Person class we can create copy of it! Additionally, when creating a copy for historical purposes, we want it to be immutable, and we intend to include information about who initiated that change and when. Let’s create a class that will be immutable and have that additional data and also change our Person class:

public class Person {
    private String id;
    private String name;
    private int salary;

    public PersonMemento createMemento() {       
        return new PersonMemento(id, name, salary);
    }
}

public class PersonMemento {
    private String id;
    private String name;
    private int salary;
    private LocalDate date;
    private String changeAuthorId;

    public PersonMemento(String id, String name) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.date = LocalDate.now();
        this.changeAuthorId = Context.getLoggedUserId();//very bad idea to get data using static methods like this, but for the sake of example is fine
    }
}

The createMemento method is now tasked with generating a copy, while the PersonMemento serves as an immutable decorator, holding both Person data and additional information about the change. The last piece of this puzzle is the History class:

public class History {
    private List<PersonMemento> mementos = new ArrayList<>();

    public void addMemento(PersonMemento personMemento) {
        this.mementos.add(personMemento);
    }

    public PersonMemento getStateOnDay(LocalDate date) {
        return this.mementos
                .stream()
                .filter(personMemento -> personMemento.getDate().isBefore(date))
                .sorted(Comparator.comparing(PersonMemento::getDate)).toList()
                .getLast();
    }

    public Person getLastStateMadeBy(Person p) {
        //similar to the getStateOnDay
    }

    public PersonMemento getLastState() {
        return this.mementos
                .stream()
                .sorted(Comparator.comparing(PersonMemento::getDate)).toList()
                .getLast();
    }
}

and client class of a whole pattern would look like this:

public static void main(String[] args) {
        History history = new History();
        Person person = new Person("111", "John Doe", 80000);
        history.addMemento(person.createMemento());//save person state to the    history before promotion
        person.promote();
        history.addMemento(person.createMemento());//save person state to the    history before promotion
        person.promote();
        System.out.println(person);// it will print salary of 320000, which is current salary
        System.out.println(history.getLastState());//it will print 160000 salary which is the last salary before current value and the last state inside the history
        //at this point history contains 2 past records(before two promotions)        
    }

Recreate Person class from the PersonMemento

You can notice that PersonMemento holds raw internal data of the Person data but we can’t perform some domain business function on that data(for example we cannot call promote() function on the PersonMemento class). Thus, we need some way to create object of class Person based on PersonMemento data. Let’s add a constructor to the Person class:


    public Person(PersonMemento memento) {
        //you can populate person fields here
    }

Conclusion

We divided responsibities. Person class has it’s own business responsibilities plus one method for copy(memento/snapshot) creation. PersonMemento holds the history of the Person state plus additional info and also it’s immutable. Finally, the History class is dedicated to managing the historical data. Now history and person are totally decoupled!