views:

296

answers:

3

I need to implement Undo/Redo frame work for my window application(editor like powerpoint), what should be the best practice to follow, how would be handle all property changes of my objects and it reflection on UI.

+3  A: 

The classic practice is to follow the Command Pattern.

You can encapsulate any object that performs an action with a command, and have it perform the reverse action with an Undo() method. You store all the actions in a stack for an easy way of rewinding through them.

womp
This doesn't allow for one way functions such as hashing or mathematical functions that can't be 'undone' using a reciprocal method. Therefore this pattern should be used with care for this approach.
BenAlabaster
That's quite true. For general UI stuff it goes a long way though.
womp
Random question from someone who never implemented one of those patterns: Couldn't the command pattern easily store a snapshot of the previous state it altered to facilitate undo? Kinda like an unholy alliance between Command and Memento? Especially if many commands can be trivially undone storing snapshots for every action might be a bit expensive.
Joey
@Johannes - I guess you could use a mashup, using the command pattern for trivial functions and the memento pattern for less trivial functions. It should perform well, but it could potentially be a headache to maintain. Considering the trivial cost of memory/disk space, I'd say go one way or the other. If you don't have one way functions, use the Command Pattern, if you do, use the Memento Pattern.
BenAlabaster
@Ben that's usually the case with the command approach. For each command, you need to store a little bit of state to undo the command. Take for example a DeleteSelectedTextCommand whose undo command would need the text that was deleted. In some cases the undo state just needs to include the whole state. But for most cases, I think your suggestion of always using full state copies is the most practical solution.
Josh Einstein
+1  A: 

Take a look at the Command Pattern. You have to encapsulate every change to your model into separate command objects.

Florian
See my comment on Womp's answer for using the command pattern for 'undo' mechanisms
BenAlabaster
+12  A: 

There are two classic patterns to use. The first is the memento pattern which is used to store snapshots of your complete object state. This is perhaps more system intensive than the command pattern, but it allows rollback very simply to an older snapshot. You could store the snapshots on disk a la PaintShop/PhotoShop or keep them in memory for smaller objects that don't require persistence. What you're doing is exactly what this pattern was designed for, so it should fit the bill slightly better than the Command Pattern suggested by others.

Also, an additional note is that because it doesn't require you to have reciprocal commands to undo something that was previously done, it means that any potentially one way functions [such as hashing or encryption] which can't be undone trivially using reciprocal commands can still be undone very simply by just rolling back to an older snapshot.

Also as pointed out, the command pattern which is potentially less resource intensive, so I will concede that in specific cases where:

  • There is a large object state to be persisted and/or
  • There are no destructive methods and
  • Where reciprocal commands can be used very trivially to reverse any action taken

the command pattern may be a better fit [but not necessarily, it will depend very much on the situation]. In other cases, I would use the memento pattern.

I would probably refrain from using a mashup of the two because I tend to care about the developer that's going to come in behind me and maintain my code as well as it being my ethical responsibility to my employer to make that process as simple and inexpensive as possible. I see a mashup of the two patterns easily becoming an unmaintainable rat hole of discomfort that would be expensive to maintain.

BenAlabaster
+1 If the state of your objects are not very large, this is drastically simpler to implement than the undo/redo command pattern which can get complex very easily. And often times you will have destructive commands that cannot be undone without a full state copy anyway. But for very large states, this can be impractical.
Josh Einstein
Take a look at purely functional data structures; they can give you considerable sharing between the current live data and the mementos.
Tommy McGuire