使用备忘录/命令模式实现快速的撤销/重做?

7
我正在为手机编写一款绘画/图形Java应用程序(因此内存有限)。该应用程序状态基本上是三个1000x500位图(即绘画的层)。加载三个位图需要大约2或3秒钟。
我试图编写一个撤销引擎,但我无法找到一个好的方法来实现。典型的方法如下:
使用命令模式:当您执行撤销操作时,重新加载初始文件的状态,然后回放到目前为止已处理的所有命令,但不包括最终命令。然而,这样做很天真,意味着等待2或3秒钟来加载初始状态,这太慢了。在移动设备上也没有足够的内存来存储初始状态。
使用备忘录模式:当您执行撤销操作时,将更改的当前状态部分替换为旧状态。这意味着每个操作都需要将旧状态的位图保存到磁盘上,因为在移动设备上没有足够的内存来存储它。由于保存位图需要时间,如果用户决定快速连续绘制多个笔划,如何应对?我不能让他们等待。
我的所有解决方案都涉及以上模式的复杂混合。
有人能否提出一个解决方案,使我可以为我的应用程序实现相对较快的撤销/重做?

没有更多建议了吗?我认为我的问题在实现任何这些模式的实际项目中都是相当基础的。 - BobDuck
可能是重复问题:如何在Java中实现简单的撤消/重做操作? - Menelaos Kotsollaris
2个回答

10

有第三种常见的撤销处理方法,那就是在Undo对象中存储两个状态之间的差异。您可以将其作为实际差异(即哪些像素已更改以及更改内容),但这可能与在每个阶段存储位图一样浪费内存。

或者,您可以使用命令模式方法,但是在撤销时不重新运行命令,而是存储命令的逆-即如果用户将红色值增加了十个,则撤销命令是将其减少十个。要撤消,只需执行逆命令。某些命令很难找到一个逆命令,例如“转换为黑白”,但通过将底层位图与许多由命令打开或关闭的过滤器混合使用,您可能可以做到。

作为另一项建议,使用您提到的命令方法,但保留先前步骤的位图。当用户执行撤消操作时,立即显示来自前(n-1)步骤的缓存位图,然后开始计算n-2的位图,以便在他再次按下撤消按钮时做好准备。


只要在用户执行操作之前,提醒用户不能撤销某些命令(例如“转换为黑白”),则这种操作是完全可接受的。 - StrixVaria
@StrixVaria:我考虑过这个问题,但我宁愿避免这种情况,因为在绘画应用程序中,用户并不期望这种功能。我认为好的iPhone应用程序都有不错的撤销功能。 - BobDuck
谢谢。存储差异似乎是备忘录模式的一种优化。我的主要操作是像1)在这些位置绘制50个蓝色涂抹2)将饱和度更改为20%;正如您所说,存储差异或编写反向操作在这里不会有太大帮助。我喜欢你的最后一个建议。这个想法的一个变体是,当用户绘制时,机器通过命令列表缓存撤消位图。但是,我需要考虑CPU和电池使用方面的问题。 - BobDuck

1
关于您使用命令模式的观点:从初始状态开始,再次执行命令根本不需要。每个Command类应该代表一个小的用户操作,并且如果要支持撤消操作,则应该具有撤消其execute()方法所做内容的机制。我们维护一堆***Command对象的堆栈。当用户撤消某些操作时,会从堆栈中弹出一个Command对象并调用其undo()方法。
我没有看到在您的情况下使用备忘录模式的任何动机,因为撤消操作将按FIFO顺序进行。我猜用户不能随心所欲地撤消操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接