public interface IUndoUnit extends IDisposable
Clients should use this interface when certain actions, changes or events should be monitored and have undoability
provided for them. If you simply want to track the state of certain items between two states of interest, you should
consider using mementos
instead.
The central methods of this interface are undo()
and redo()
which contain the logic to undo or redo a
unit of work. Keep in mind that undo/redo are sensible operations and should maintain a consistent state before and
after each operation since they may be executed potentially often one after another.
The requirement for an IUndoUnit
is that when a program is in a certain state and a call to undo()
is
followed by a call to redo()
then the program is in the exact same state as before (the same holds true for the
other way around).
A default implementation of this interface is the abstract class AbstractUndoUnit
which forces only the
implementation of the undo/redo logic and defaults the other methods. Clients should primarily extend this class instead
of implementing the whole interface.
IUndoUnits are managed by the UndoEngine
. Custom units can always be added to the engine using the method
UndoEngine.addUnit(IUndoUnit)
. Also consider to use one of the convenience implementations: ConsumerUndoUnit
takes delegates for the undo/redo operations and CompositeUndoUnit
creates a bracketing unit comprising several
other units.
Also note that in order to keep a consistent state, methods of objects of this type should not be called by clientcode
directly but use the UndoEngine
instead.
Modifier and Type | Method and Description |
---|---|
String |
getRedoName()
Returns the name of the redo unit.
|
String |
getUndoName()
Returns the name of the undo unit.
|
void |
redo()
Redoes the work that is represented by this unit.
|
boolean |
tryMergeUnit(IUndoUnit unit)
Tries to merge the given unit with this one.
|
boolean |
tryReplaceUnit(IUndoUnit unit)
Tries to replace the given unit with this one.
|
void |
undo()
Undoes the work that is represented by this unit.
|
close, dispose
String getRedoName()
Depending on the implementation and context this might be a human readable representation of the redo action or a symbolic name that needs localization.
String getUndoName()
Depending on the implementation and context this might be a human readable representation of the undo action or a symbolic name that needs localization.
void redo()
Undo/redo are sensible operations and should maintain a consistent state before and after each operation since they may be executed potentially often one after another.
getRedoName()
boolean tryMergeUnit(IUndoUnit unit)
This method is called when this
unit is the head of an UndoEngine
's or
CompositeUndoUnit
's queue and a new unit is added. It is meant to try to incorporate
the change of the given unit
into this
and if successful return true
. This should be the case
when the end state of this
unit is equal to the start state of the given unit
.
For example, if this
unit is the head of the queue and represents the work from state A
to state B
and the given unit the work from state B
to state C
, then this method should try to make this
unit shift from state A
to state C
. If this is successful, the method is expected to return
true
. The other unit is then disposed
by the UndoEngine
afterwards.
Clients don't necessarily have to implement this method if the unit doesn't happen very often. In fact, the default
implementation of AbstractUndoUnit.tryMergeUnit(IUndoUnit)
simply returns false
.
Implementing this method faithfully will cause multiple units of work to inseparably appear as one and undoing/redoing
it will undo/redo the work of all collapsed units. Depending on the situation this may be reasonable, for example when
there are potentially a lot of changes where not every intermediate step is required to be recorded. In this case
implementing this method faithfully will greatly improve the performance and reduce the required amount of memory of the
undo process. If you want to group together multiple units as a single block but still want to be able to separate each
step, consider to use a CompositeUndoUnit
instead.
unit
- The unit to incorporate that happened after this unit.unit
has been incorporated into this unit and unit
can be disposed of.boolean tryReplaceUnit(IUndoUnit unit)
This method is called if a newly added unit couldn't be merged
with the head unit of
the queue (i.e. returned false). Instead of trying to merge a newly added unit, this method tries to replace the given
unit (which is the head of the queue) with this
by incorporating the change of the given unit
into this
and if successful return true
. This should be the case when the start state of this
unit is equal to the
end state of the given unit
.
For example, if the given unit
represents the work from state A
to state B
and this
unit
the work from state B
to state C
, then this method should try to make this
unit shift from state
A
to state C
. If this is successful, the method is expected to return true
. The given unit
is then disposed
by the UndoEngine
afterwards and replaced
with this
unit in the queue.
Clients don't necessarily have to implement this method if the unit doesn't happen very often. In fact, the default
implementation of AbstractUndoUnit.tryReplaceUnit(IUndoUnit)
simply returns
false
. Implementing this method faithfully will cause multiple units of work to inseparably appear as one
and undoing/redoing it will undo/redo the work of all collapsed units. Depending on the situation this may be
reasonable, for example when there are potentially a lot of changes where not every intermediate step is required to be
recorded. In this case implementing this method faithfully will greatly improve the performance and reduce the required
amount of memory of the undo process. If you want to group together multiple units as a single block but still want to
be able to separate each step, consider to use a CompositeUndoUnit
instead.
unit
- The unit to incorporate that happened before this unit.this
unit has been incorporated into the given unit
and this
can be
disposed of.void undo()
Undo/redo are sensible operations and should maintain a consistent state before and after each operation since they may be executed potentially often one after another.
getUndoName()