Pages

Wednesday, August 29, 2012

Memento design pattern tutorial

When you develop an application you might want some objects to save and restore their states. One way to do that is to send the initial object state to another object. But next problems occur:
  • initial object structure becomes revealed
  • another object might change received state
It is much better to save object state with special container object based on Memento design pattern.

This post is available in Russian.


Memento


Type: behavioral

Purpose: to preserve a “snapshot” of an object’s state, so that the object can return to its original state without having to reveal its content to the rest of the world.

Alternative names: Token, Snapshot

Implementation:
Originator - creates Memento and uses this Memento to later restore its state.

Memento - static inner class of the Originator and holder of the Originator’s state. The Originator determines how much is stored in the Memento and only the Originator should be able to read the Memento. State within the Memento should be inaccessible to everybody except the Originator.

StateHolder - the object that wishes to preserve the state. It never needs to know what is within a Memento; it only needs to know that the object it receives enables it to restore the state of the Originator.


As before we will continue our war enginery stuff. Create a class that describes a tank. Our tank has two properties - speed and power. Current values of these properties are stored in an instance of TankMemento inner class which is created during getMemento() method invocation. Method setMemento() takes an instance of Object as an argument. It changes properties of Tank object if argument is a TankMemento:
public class Tank {
    private int speed;
    private int power;

    public Tank(int speed, int power) {
        this.speed = speed;
        this.power = power;
    }

    private class TankMemento {
        private int speedState;
        private int powerState;

        private TankMemento(int speedState, int powerState) {
            this.speedState = speedState;
            this.powerState = powerState;
        }
    }

    public Object getMemento() {
        return new TankMemento(speed, power);
    }

    public void setMemento(Object object) {
        if (object instanceof TankMemento) {
            TankMemento memento = (TankMemento) object;

            speed = memento.speedState;
            power = memento.powerState;
        }
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getPower() {
        return power;
    }

    public void setPower(int power) {
        this.power = power;
    }

    public String getDescription() {
        return "Speed is " + speed + " km/h, power is " + power;
    }
}
Class Tank is an Originator, TankMemento is a Memento. It is a simple example so that we do not need even a StateHolder object to store the state.

Create class MementoDemo to demonstrate pattern on the go. Create a Tank instance with defined state - 50 km/h speed and power of 50 some damage power units. Save current tank state with invocation of getMemento() method. memento object's type is Object and its inner implementation is unknown. It has no methods to change its properties. The only known thing is that it stores the state of tank object. After we saved tank's state let's change it - for example, our tank was damaged and its speed and power have decreased for 50%. But in some time the tank was repaired and its state was restored with invocation of setMemento() method and its argument - memento object:
public class MementoDemo {

    public static void main(String[] atgs) {
        Tank tank = new Tank(50, 50);
        System.out.println("Initial tank description:");
        System.out.println(tank.getDescription() + "\n");

        Object memento = tank.getMemento();

        tank.setSpeed(25);
        tank.setPower(25);
        System.out.println("New tank description:");
        System.out.println(tank.getDescription() + "\n");

        tank.setMemento(memento);

        System.out.println("Restored tank description:");
        System.out.println(tank.getDescription());
    }
}

Execute method main():











Folder structure:














Application source code is available at http://code.google.com/p/memento-demo/.

No comments:

Post a Comment