理解Swing GUI组件如何最大限度的使用Command模式获得undo/redo功能
By Tomer Meshorer
翻译:flyingbug
第三部分:Swing中使用undo/redo机制
在Swing中,用户动作的结果分别存储在不同的监听器的实现中。每个动作的结果被存储在一个实现了UndoableEdit接口对象中。在过去,这事由被实例化并注册到一个AWT组件中的单独的命令对象(事件监听器)来执行;无论何时,每次这个命令对象被调用,都会添加一个新的UndoableEdit对象来描述这个操作的结果。这些结果对象代替了过去存储在命令对象中的本地系统信息。
下面是一个描述Swing undo机制的UML类图:

Swing's undo mechanism class diagram |
下面解释它如何工作:
当我提及过去的某一时刻,用户的动作结果存储在一个UndoableEdit对象中。为了方便,Swing提供了一个AbstractUndoableEdit抽象类来提供UndoableEdit接口的缺省实现,开发者可以简单的继承AbstractUndoableEdit类并重载需要的方法,而不必直接实现UndoableEdit接口。
为了支持特定的编辑操作(一个编辑操作作为一个可被用户调用的命令的结果,比如:插入或者剪切文本),开发者必须提供一个AbstractUndoableEdit的子类来包装该命令的结果信息,并为取消这个结果提供相应的手段。
有时,一个编辑操作可能由一系列的其他简单操作组成(比如:一个全局替换操作是由一系列单独的替换操作组成的)。这种复合类型的操作将被CompoundEdit捕获。这个类实现UndoableEdit并且覆写了undo()方法,在其中按逆序为它的每个子操作调用undo()方法
下面介绍Swing undo机制的undo listener。Undo listeners是一些实现了UndoableEditListener接口并且每当发生undo操作的时候捕获UndoableEditEvent对象。UndoManager是另外一个特殊类型的listener。当这个listener接受到一个UndoableEditEvent对象时,它从事件中取出事件的操作并将其储存在一个队列当中。More about the UndoManager in a moment.
一个支持undoable操作的应用程序必须提供addUndoableEditListener() 和removeUndoableEditListener()方法供UndoManager注册。
与javaBeans事件模型类似的,Swing提供一个UndoableEditSupport类以便用户方便的管理使用到的listeners。用户可以通过addUndoableListener()注册自己的listeners,也可以通过removeUndoableListener()删除listeners。要将一个操作通知已注册的listener只需简单的调用postEdit()方法。这个方法自动生成一个UndoableEditEvent并且将其传递给每个listener的undoableEditHappened()方法。另外,这个类对复合操作提供简单的支持,允许一系列的简单操作自动的组合到一个CompoundEdit类中。
最后,Swing通过UndoManager类对应用程序的多级undo操作提供方便的支持,UndoManager类实现UndoableEditListener接口并按列表的形式组织历史操作。UndoManager类避免用户手工编写代码记录用户的所有操作;它自动的记录所有的操作并且提供指向当前undo和redo操作的引用。每当一个可以undo的操作出现(例如:一个新的元素被加到列表中) UndoManager类便将此操作加到其内部的队列中保存。用户可以通过setLimit()方法限制每个manager类中队列的长度(即可保存的操作的数量)。
下面描述了一个undo队列的工作情形.

Inside the undo queue: We start from an empty queue, add some actions, perform undo, and then perform another action. |
现在我们将它应用到一个具体的实例当中。
见下章...