image – making undo in python
image – making undo in python
The canonical strategy is to use the Command pattern. Youll represent the things you can do as Command objects, and each object is placed on a stack. The state of the application is then defined by an initial state plus everything that the stack has. Thus, the undo operation is then just popping the top stack item and reapplying the remaining items to the initial state.
In practice, sometimes its expensive to keep applying those operations to the initial state to generate the current state. For example, this might be true with something like a complex series of image adjustments, like you might find in Photoshop. In cases like that, its common to keep an alternating state-stack series in memory:
+---------+
| state_0 |
+---------+ +---------+
| next -------> | stack_0 |
+---------+ +---------+
| data | +---------+
| next -------> | state_1 |
+---------+ +---------+
| next ----- ...
+---------+
Each stack_i
holds commands until it exceeds some pre-set complexity (e.g., the commands exceed computational cost X
) or ordinal (e.g. the stack holds X
or more commands) limit. At that point, a new intermediate state object state_i+1
is created to encapsulate the state, and a new, empty stack stack_i+1
is created to hold new commands.
In this way, you only have to apply a small sequence of commands to the last snapshotted state to get the current state. This comes at the expense of memorizing entire states, which may not be feasible for very large applications, but you can choose to snapshot only a set of the state to optimize.
Also, keep in mind that Pythons functions are first-class objects, which can make implementing the Command pattern very smooth:
actions = []
def do_action1(arg1, arg2):
# .. do the action ..
# remember we did the action:
actions.append(do_action1, (arg1, arg2))
def do_action2(arg1, arg2):
# .. do the action ..
actions.append(do_action2, (arg1, arg2))
def replay_actions():
for fn, args in actions:
fn(*args)