I would like to implement undo/redo in a small paint application. It seems the Command Pattern fits the use nicely, but I am unsure how to best implement it.
As I understand the pattern, it is necessary to include in each command:
- The details of the paint operation for purposes of redo (e.g. Line -> start & end points, free form line ->
GeneralPath
) - The state of the component prior to the change for undo. In this case, that will be a small snapshot image of the area affected by the command.
My understanding based on that is that each command needs to be 'atomic' or self contained, with all the information needed for undo/redo that operation.
Unfortunately that would require storing more information than I'd first anticipated. For a line we must also account for things like the Color
, Stroke
and RenderingHints
used to draw it initially. This turns my 'simple little commands' into something ..more bulky in memory, and with more boiler-plate code to churn out (each will be a serializable bean1).
For reasons of memory conservation (mostly) I was wanting to 'cheat' on the specification of the commands. Perhaps take a backup of the entire drawing area every 100th update, but otherwise store no part of the changed image, and simply rebuild the last (up to) 100 commands for each new paint operation. But that seems problematic to ensure that the state of the Graphics
object is right before painting each part - this part might require a line, but the RenderingHints
were changed 4 commands ago, the Color
was changed 98 commands ago, while the Stroke
has remained the same for the last 227 commands.
Pursuing a more memory efficient command seems to throw the pattern right out the window in terms of being 'atomic'. That in turn leads to difficulties in determining the earliest command that might affect the rendering.
Should I:
- Look for a new pattern?
- Attempt to implement my peculiar needs by tweaking the pattern?
- Toss all this in the waste bin as premature optimization and code it in the simplest (and most memory consuming) way that sticks to the command pattern as defined?
Update
- "each will be a serializable bean" On 2nd thoughts, no. I did dome checks to find that a
Graphics2D
(which neatly encapsulates many parameters used when drawing) is not serializable. Further, aBasicStroke
is serializable, but the thickness of the stroke is not stored. I could create serializable versions of many of the attributes but it seems to make for a lot more code, so I'm going to abandon that spec. as well. I will only attempt to store a reference to aBufferedImage
at run-time.