javamemento

Memento in java. Mutable state


I am trying to understand the Memento Pattern. For that purpose i am trying to implement the undo function. The problem is that when ever i save the old state of the originator in the queue and run a different function the save state changes to the current state. I really need help to understand what i am doing wrong. How can i make the vector to be immutable.

This is the memento class.

package memento;

public class Memento
{
    private final boolean[] vectorState;

    public Memento(boolean[] vector) {vectorState = vector;}

    boolean[] getMemento() { return vectorState;}

}

The Originator just need to shift a vector of boolean to the left. (TRUE,FALSE,FALSE) shift left returns: (FALSE,FALSE,TRUE). This is the implementation.

package memento;

public class ShilftLeftOriginator
{
    private boolean[] vector;

    public ShilftLeftOriginator(boolean[] vector) {this.vector = vector;}

    public void execute()
    {
        final boolean firstValue = this.vector[0];
        for (int i = 1; i < this.vector.length; i++) {
            this.vector[i - 1] = this.vector[i];
        }
        this.vector[vector.length - 1] = firstValue;
    }


    public Memento saveToMemento() {return new Memento(vector);}

}

And the caretaker:

package memento;

import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;


public final class BooleanVector {
    private boolean[] vector;
    private Deque<Memento> mementoList = new LinkedList<>();

    public BooleanVector(boolean[] inputValues) {
        this.vector = inputValues;
    }


    @Override
    public boolean equals(Object obj) 
    {
        if (obj == null) return false;
        if (!(obj instanceof BooleanVector)) return false;
        BooleanVector otherVector = (BooleanVector) obj;
        return Arrays.equals(this.vector, otherVector.vector);
    }

    public void shiftLeft() 
    {
        ShilftLeftOriginator shiftLeft = new ShilftLeftOriginator(vector);
        mementoList.add(shiftLeft.saveToMemento());
        shiftLeft.execute(); // This is my Problem. After execute ist call the value(vector) in mementoList changes             
    }

    public void undo(){ this.vector =  mementoList.pop().getMemento();}

}

And now the test class and the error that i am receiving.

package memento;

public class Main {

    public static void main(String[] args) {
        boolean[] inputValues = { false, true, false };
        BooleanVector vector = new BooleanVector(inputValues);

        vector.shiftLeft();

        boolean[] expectedValues = new boolean[] { true, false, false };
        BooleanVector expectedVector = new BooleanVector(expectedValues);

        if (!vector.equals(expectedVector)) {
            throw new IllegalStateException(vector.toString());
        } else {
            System.out.println("shiftleft working");
        }

        vector.undo();

        expectedValues = new boolean[] { false, true, false };
        expectedVector = new BooleanVector(expectedValues);

        if (!vector.equals(expectedVector)) {
            throw new IllegalStateException(vector.toString());
        } else {
            System.out.println("undo working");
        }

    }

}

The console output:

shiftleft working
Exception in thread "main" java.lang.IllegalStateException: [true, false, false]
    at memento.Main.main(Main.java:26)

Solution

  • The problem is that you're always manipulating the same array. So if you shift left, you also shift left the array you stored in the Memento object, because it's all the same array.

    To solve it, make a copy of the array in the constructor of the Memento object:

    public Memento(boolean[] vector) {
        vectorState = Arrays.copyOf(vector, vector.length);
    }
    

    Aside from that, you seem to have your classes mixed up. BooleanVector and ShilftLeftOriginator are the originator, while Main is the caretaker.