Java
Usage examples: The Memento’s principle can be achieved using serialization, which is quite common in Java. While it’s not the only and the most efficient way to make snapshots of an object’s state, it still allows storing state backups while protecting the originator’s structure from other objects.
Here are some examples of the pattern in core Java libraries:
All
java.io.Serializableimplementations can simulate the Memento.All
javax.faces.component.StateHolderimplementations.
Shape editor and complex undo/redo
This graphical editor allows changing the color and position of the shapes on the screen. Any modification can be undone and repeated, though.
The “undo” is based on the collaboration between the Memento and Command patterns. The editor tracks a history of performed commands. Before executing any command, it makes a backup and connects it to the command object. After the execution, it pushes the executed command into history.
When a user requests the undo, the editor fetches a recent command from the history and restores the state from the backup kept inside that command. If the user requests another undo, the editor takes a following command from the history and so on.
Reverted commands are kept in history until the user makes some modifications to the shapes on the screen. This is crucial for redoing undone commands.
editor
editor/Editor.java: Editor code
package refactoring_guru.memento.example.editor;
import refactoring_guru.memento.example.commands.Command;
import refactoring_guru.memento.example.history.History;
import refactoring_guru.memento.example.history.Memento;
import refactoring_guru.memento.example.shapes.CompoundShape;
import refactoring_guru.memento.example.shapes.Shape;
import javax.swing.*;
import java.io.*;
import java.util.Base64;
public class Editor extends JComponent {
private Canvas canvas;
private CompoundShape allShapes = new CompoundShape();
private History history;
public Editor() {
canvas = new Canvas(this);
history = new History();
}
public void loadShapes(Shape... shapes) {
allShapes.clear();
allShapes.add(shapes);
canvas.refresh();
}
public CompoundShape getShapes() {
return allShapes;
}
public void execute(Command c) {
history.push(c, new Memento(this));
c.execute();
}
public void undo() {
if (history.undo())
canvas.repaint();
}
public void redo() {
if (history.redo())
canvas.repaint();
}
public String backup() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this.allShapes);
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (IOException e) {
return "";
}
}
public void restore(String state) {
try {
byte[] data = Base64.getDecoder().decode(state);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
this.allShapes = (CompoundShape) ois.readObject();
ois.close();
} catch (ClassNotFoundException e) {
System.out.print("ClassNotFoundException occurred.");
} catch (IOException e) {
System.out.print("IOException occurred.");
}
}
}editor/Canvas.java: Canvas code
history
history/History.java: History stores commands and mementos
history/Memento.java: Memento class
commands
commands/Command.java: Base command class
commands/ColorCommand.java: Changes color of selected shape
commands/MoveCommand.java: Moves selected shape
shapes: Various shapes
shapes/Shape.java
shapes/BaseShape.java
shapes/Circle.java
shapes/Dot.java
shapes/Rectangle.java
shapes/CompoundShape.java
Demo.java: Initialization code
OutputDemo.png: Screenshot

Last updated