Memento


Definition

To record an object internal state without violating encapsulation and reclaim it later without knowledge of the original object.

Where to use & benefits

Example

To design a program with Memento feature is used to combine several design patterns like Command, Mediator or Iterator.

Here is an example expanded from Mediator example.

To show the Memento design concept, we record a dice number. This is very simple program. The Memento class just holds a number.

class Memento {
    int num;
    Memento(int c) {
       num = c;
    }
    int getNum() {
       return num;
    }
}

Then we combine Mediator and Command patterns to design three buttons and one label.The first button throws dice, the second button shows the dice number backward, and the third button clears number displayed. The label is used to display the dice number thrown. We use Math.random() method to get number from 1 to 6.

class BtnDice extends JButton implements Command {
    Mediator med;
    BtnDice(ActionListener al, Mediator m) {
        super("Throw Dice");
        addActionListener(al);
        med = m;
        med.registerDice(this);
    }
    public void execute() {
       med.throwit();
    }
}

class BtnClear extends JButton implements Command {
    Mediator med;
    BtnClear(ActionListener al, Mediator m) {
        super("Clear");
        addActionListener(al);
        med = m;
        med.registerClear(this);
    }
    public void execute() {
       med.clear();
    }
}
class BtnPrevious extends JButton implements Command {
    Mediator med;
    BtnPrevious(ActionListener al, Mediator m) {
        super("Previous");
        addActionListener(al);
        med = m;
        med.registerPrevious(this);
    }
    public void execute() {
       med.previous();
    }
}
class LblDisplay extends JLabel{
    Mediator med;
    LblDisplay (Mediator m) {
        super("0",JLabel.CENTER);
        med = m;
        med.registerDisplay(this);
        setBackground(Color.white);
        setBorder(new EtchedBorder(Color.blue, Color.green));
        Font font = new Font("Arial",Font.BOLD,40);
        setFont(font);
    }
}

The Mediator class will hold these participating objects and manipulate their relationships.

class Mediator {
    BtnDice btnDice;
    BtnPrevious btnPrevious;
    BtnClear btnClear;
    LblDisplay show;
    java.util.List list, undo;
    boolean restart = true;
    int counter = 0, ct = 0;
    //....
    Mediator() {
       list = new ArrayList();
       undo = new ArrayList();
    }
    void registerDice(BtnDice d) {
        btnDice = d;
    }
    void registerClear(BtnClear c) {
        btnClear = c;
    }
    void registerPrevious(BtnPrevious p) {
        btnPrevious = p;
    }
    void registerDisplay(LblDisplay d) {
        show = d;
    }
    void throwit() {
       show.setForeground(Color.black);
       int num = (int)(Math.random()*6 +1);
       int i = counter++;
       list.add(i, new Integer(num));
       undo.add(i, new Memento(num));
       show.setText(""+num);
    }
    
    void previous() {
       show.setForeground(Color.red);
       btnDice.setEnabled(false);
       if (undo.size() > 0) {       
           ct = undo.size()-1;
           Memento num = (Memento)undo.get(ct);
           show.setText(""+num.getNum());
           undo.remove(ct);
       }
       if (undo.size() == 0)
           show.setText("0");
    }
    void clear() {
        list = new ArrayList();
        undo = new ArrayList();
        counter = 0;
        show.setText("0");
        btnDice.setEnabled(true);
    }   
}

Finally, write a demo class.

class MementoDemo extends JFrame implements ActionListener {
    Mediator med = new Mediator();
    MementoDemo() {
       JPanel p = new JPanel();
       p.add(new BtnDice(this,med));
       p.add(new BtnPrevious(this,med));
       p.add(new BtnClear(this,med));
       JPanel dice = new JPanel();
       LblDisplay lbl = new LblDisplay(med);
       dice.add(lbl);
       getContentPane().add(dice, "Center");
       getContentPane().add(p, "South");
       setTitle("Memento pattern example");
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setSize(400,200);
       setVisible(true);
    }
    public void actionPerformed(ActionEvent ae) {
        Command comd = (Command)ae.getSource();
        comd.execute();
    }
    public static void main(String[] args) {
        new MementoDemo();
    }
}

The complete workable program is as follows. Copy it, compile it and run it.

import javax.swing.*;
import java.awt.event.*;
import java.awt.FontMetrics;
import java.awt.*;
import java.util.*;
import javax.swing.border.*;

interface Command {
    void execute();
}
class Mediator {
    BtnDice btnDice;
    BtnPrevious btnPrevious;
    BtnClear btnClear;
    LblDisplay show;
    java.util.List list, undo;
    boolean restart = true;
    int counter = 0, ct = 0;
    //....
    Mediator() {
       list = new ArrayList();
       undo = new ArrayList();
    }
    void registerDice(BtnDice d) {
        btnDice = d;
    }
    void registerClear(BtnClear c) {
        btnClear = c;
    }
    void registerPrevious(BtnPrevious p) {
        btnPrevious = p;
    }
    void registerDisplay(LblDisplay d) {
        show = d;
    }
    void throwit() {
       show.setForeground(Color.black);
       int num = (int)(Math.random()*6 +1);
       int i = counter++;
       list.add(i, new Integer(num));
       undo.add(i, new Memento(num));
       show.setText(""+num);
    }
    
    void previous() {
       show.setForeground(Color.red);
       btnDice.setEnabled(false);
       if (undo.size() > 0) {       
           ct = undo.size()-1;
           Memento num = (Memento)undo.get(ct);
           show.setText(""+num.getNum());
           undo.remove(ct);
       }
       if (undo.size() == 0)
           show.setText("0");
    }
    void clear() {
        list = new ArrayList();
        undo = new ArrayList();
        counter = 0;
        show.setText("0");
        btnDice.setEnabled(true);
    }
    
}
class BtnDice extends JButton implements Command {
    Mediator med;
    BtnDice(ActionListener al, Mediator m) {
        super("Throw Dice");
        addActionListener(al);
        med = m;
        med.registerDice(this);
    }
    public void execute() {
       med.throwit();
    }
}

class BtnClear extends JButton implements Command {
    Mediator med;
    BtnClear(ActionListener al, Mediator m) {
        super("Clear");
        addActionListener(al);
        med = m;
        med.registerClear(this);
    }
    public void execute() {
       med.clear();
    }
}
class BtnPrevious extends JButton implements Command {
    Mediator med;
    BtnPrevious(ActionListener al, Mediator m) {
        super("Previous");
        addActionListener(al);
        med = m;
        med.registerPrevious(this);
    }
    public void execute() {
       med.previous();
    }
}
class Memento {
    int num;
    Memento(int c) {
       num = c;
    }
    int getNum() {
       return num;
    }
}
class LblDisplay extends JLabel{
    Mediator med;
    LblDisplay (Mediator m) {
        super("0",JLabel.CENTER);
        med = m;
        med.registerDisplay(this);
        setBackground(Color.white);
        setBorder(new EtchedBorder(Color.blue, Color.green));
        Font font = new Font("Arial",Font.BOLD,40);
        setFont(font);
    }
}
class MementoDemo extends JFrame implements ActionListener {
    Mediator med = new Mediator();
    MementoDemo() {
       JPanel p = new JPanel();
       p.add(new BtnDice(this,med));
       p.add(new BtnPrevious(this,med));
       p.add(new BtnClear(this,med));
       JPanel dice = new JPanel();
       LblDisplay lbl = new LblDisplay(med);
       dice.add(lbl);
       getContentPane().add(dice, "Center");
       getContentPane().add(p, "South");
       setTitle("Memento pattern example");
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setSize(400,200);
       setVisible(true);
    }
    public void actionPerformed(ActionEvent ae) {
        Command comd = (Command)ae.getSource();
        comd.execute();
    }
    public static void main(String[] args) {
        new MementoDemo();
    }
}

Return to top