Working With Design Patterns: Memento
When you undo a command, it needs to reset the state of the PathCanvas to a prior point in time. A stored Memento object represents this prior point. The method setMemento updates the state of the PathCanvas object.
Listing 4: PathCanvas.
import java.awt.*; import java.awt.geom.*; import java.util.*; import java.util.List; public class PathCanvas extends Observable implements MementoOriginator, Displayable { private List<ColoredPath> paths = new ArrayList<ColoredPath>(); public List<ColoredPath> paths() { return paths; } public void add(ColoredPath path) { paths.add(path); changed(); } public void displayOn(Graphics2D graphics) { for (ColoredPath path: paths) { Area polygon = new Area(path.getGeneralPath()); graphics.setColor(path.getColor()); graphics.fill(polygon); } } public Memento getMemento() { List<ColoredPath> pathCopies = new ArrayList<ColoredPath>(); pathCopies.addAll(paths); return new Memento(pathCopies); } @Override public void setMemento(Memento memento) { paths.clear(); paths.addAll(memento.getPaths()); changed(); } private void changed() { setChanged(); notifyObservers(); } }
I created the MementoOriginator interface not out of necessity, but to reinforce the class names used in the Design Patterns book. The interface is simply:
public interface MementoOriginator { Memento getMemento(); void setMemento(Memento memento); }
Listing 5: Memento.
import java.util.*; public class Memento { private List<ColoredPath> paths; public Memento(List<ColoredPath> paths) { this.paths = paths; } public List<ColoredPath> getPaths() { return paths; } }
The most involved piece of the puzzle is the set of commands themselves. I implemented TriangleCommand and SquareCommand to demonstrate drawing a couple different shapes. Because each command needs to support being executed, undone, and redone, I was able to move some commonality into a superclass AbstractCommand (see Listing 6).
Listing 6: AbstractCommand.
import java.awt.*; import java.util.*; import model.*; import model.PathCanvas; import commandframework.*; abstract public class AbstractCommand implements Command { private Memento memento; private Random random = new Random(); protected double width; protected double height; protected PathCanvas canvas; protected Color color; public AbstractCommand() { } public AbstractCommand(PathCanvas canvas, double width, double height, Color color) { this.width = width; this.height = height; this.canvas = canvas; this.color = color; } public void execute() { this.memento = canvas.getMemento(); transform(); } abstract protected void transform(); public void undo() { Memento redoMemento = canvas.getMemento(); canvas.setMemento(memento); memento = redoMemento; } public void redo() { Memento undoMemento = canvas.getMemento(); canvas.setMemento(memento); memento = undoMemento; } protected double random(double max) { return random.nextDouble() * max; } }
Page 2 of 3
This article was originally published on January 10, 2008