Command是我们最常用的模式之一,主要用在 撤消/重做功能Undo | Redo,其中有代表的是二维/三维的图形操作,它为用户提供了必要的,可修改的操作接口,大大提高了软件的灵活性。Undo | Redo机制一般需要借助堆栈(或者队列,链表)等数据结构,通过记录每一步的操作来实现。
图形操作一般是通过图元属性的更改完成(比如move),这种操作称为可逆操作,只需要记录改变之前的图元属性即可。而不规则图元的变化,可能 需要记录整体的描述信息,无法用属性参数描述,称之为不可逆。
不扯闲篇,直接进主题。
一. 图元组织结构
图元是描述对象的基本单位,可以抽象为对象属性的变化量,比如我们将图元的操作表述为 Create、Delete、Move、Rotate等。这些操作描述为统一的Graphics上层接口,即描述为命令(Command),图元类组织如下:
对于一个系统命令,根据不同的操作调用图元对象执行命令,然后进行操作的记录和处理:
• New & Delete:
为了对数据进行管理,我们需要增加一个id项,用于识别当前的数据对象。
添加删除标记位,删除时只是修改可见性(从Undo | Redo考虑),在Command真正出栈的时候才真正执行删除。
• Modify操作:
对于可逆操作,需要通过记录参数确定,比如Move的(dx,dy)Rotate对应角度等,仅需要简单的记录其参数值就能够进行描述,对于旋转、平移等频繁的连续微操作需要考虑命令的合并。
对于非可逆操作,可以通过对象记录来完成,建立对象的MemList链表(限定步数的用数组即可),用来记录每一步的变化情况。
• 复制、剪切、粘贴:
需要维护一个剪贴板,记录保存一组数据对象的信息即可。复制和剪切时,将数据信息写入,粘贴的时候读取,并执行相应的操作。
二. Command命令抽象
Command类 封装了操作类型、操作对象列表、参数列表、标签等命令属性和Execute(),Unexecute(),Combine()等抽象方法。
三. Command管理
Manager类是管理Command对象的核心类,是应用程序或者物体模型与Undo机制间的桥梁,其中:
a)nMaxStep变量表示允许Undo | Redo的次数,nCount变量表示当前记录个数,nCurrent变量表示当前的Undo的位置(从0开始计数);
b)函数Undo()和Redo()用于响应来自系统菜单,快捷键或者工具条的Undo和Redo命令;
c)函数SetMaxStep()用于设置允许Undo | Redo的次数;
d)Undo_Available和Redo_Available判断Undo | Redo操作是否有效;Undo操作执行当前CommandRecord的UnExecute()操作,Redo操作执行上面CommandRecord的Execute()操作。
Manager类 维护一个CommandList(一般用dequeue实现),栈内元素是一个个的操作(命令),当命令满时,自动删除最下面的元素,因此操作所能恢复的步数不会超过设定的堆栈长度。
Undo | Redo过程除了要执行上面的堆栈操作以外,还需要执行以下命令:
a)查找当前命令所影响的图元链表(GraphList),如果是Undo命令,将链表中的每一个图元恢复到当前状态的上一状态。如果是Redo命令,恢复到当前的下一个状态,并把恢复后的状态结点标记为current。
b)如果用户点了保存或者在Undo后执行了新操作,那么系统将删除保存的堆栈命令以及这些命令对应的图元状态信息。
示例代码 可以从作者Github下载:https://github.com/linolzhang/Command