[PEAK] Commands, Actions, Undo/Redo, and transactions

Niki Spahiev niki at vintech.bg
Thu Jul 29 04:30:56 EDT 2004


Some comments from GUI front.

Phillip J. Eby wrote:
> I've pretty much decided that "command" sucks as a term and as a 
> metaphor for how workspaces will track undo/redo and transactions.  For 
> one thing, most of what I've been calling commands aren't truly commands 
> in the "Command Pattern" sense, since they don't know how to "execute 
> themselves".
> 
> So, I'm going to define a different terminology:
> 
> 
>   class IAction(Interface):
>       """A behavior unit that can be undone or redone"""
> 
>       undoable = Attribute("""True if this action can potentially be 
> undone""")
> 
>       key = Attribute(
>           """A unique key identifying the subject of this action, or 
> None"""
>       )
> 
>       def undo():
>           """Undo the behavior"""
> 
>       def redo():
>           """Reapply the behavior"""


Will there be do() method? Or is redo() first time considered do()?
Also action has some different meaning in GUI world see QAction from QT.
IMHO better stick with pattern names.

[...]

>   class IUndoManager(Interface):
> 
>       """Perform undo/redo of recorded actions"""
> 
>       def undoLast():
>           """Undo the last action added to the undo stack
>           (while moving it to the redo stack)"""
> 
>       def redoNext():
>           """Pop an action from the redo stack and redo it
>           (while moving it to the undo stack)"""
> 
>       def add(actionGroup):
>           """Record IActionGroup actionGroup as part of the history
> 
>           (add to undo stack, clear redo stack, and if action
>            isn't undoable, clear the undo stack too.)"""
> 
>       # XXX haveUndoables(), haveRedoables(), clear()?

Other useful concept is SavePoint - position in undo stack that 
corresponds to externally saved state. It needs markSavePoint() and 
isSavePoint()

[...]

> Because a state-based action simply puts things back the way they were 
> when undone, there's no need to have more than one state-based action 
> per target state per action group.  That's what the 'key' is for.  You 
> don't create a new action if the group already has an action for that 
> key, and when the group is added (committed) to a larger group, it can 
> omit any actions that already have a matching action in the larger 
> group.  (Because action groups are undone or redone as a unit, there's 
> no need to keep an action that represents a snapshot somewhere in the 
> middle of the action.)

I have some unclear thoughts about hierarchies. If keys used are like 
URIs and correspond to model nesting then having state for key=a/b/c 
implies that state for key=a/b/c/d is not needed as its part of a/b/c

> Change-based actions are simpler than state-based actions, but they can 
> consume more time and space if objects change frequently within a 
> top-level action group.  Change-based actions only know how to do 
> something forwards or backwards, and they are always added to the 
> current action group regardless of whether similar actions already 
> appear in the group.  They have a key of 'None', so they also don't get 
> aggregated when an action group is merged with a parent action group.

For change-based actions very usable is merging. Sequence of actions 
with same key is merged as this:

a -> b
b -> c

becomes single action a -> c

> Whew!  I think that about covers it.  I think this actually constitutes 
> a "best of breed" API, in that it covers scenarios ranging from object 
> prevalence, to GUIs with undo/redo, to "typical" database applications, 
> to esoteric nested transaction needs.

After hearing GUI could not resist :^)

Niki Spahiev



More information about the PEAK mailing list