撤消重做实现的最佳实践

Fir*_*roz 17 c# frameworks undo-redo

我需要为我的窗口应用程序(像powerpoint这样的编辑器)实现撤消/重做框架工作,应该遵循的最佳实践,如何处理我的对象的所有属性更改以及它在UI上的反射.

Ben*_*ter 30

有两种经典模式可供使用.第一个是memento模式,用于存储完整对象状态的快照.这可能比命令模式更加系统密集,但它允许非常简单地回滚到较旧的快照.您可以将快照存储在磁盘上,然后将其存储在PaintShop/PhotoShop中,或者将它们保存在内存中,以用于不需要持久性的较小对象.你正在做的正是这个模式的设计,所以它应该比其他人建议的命令模式略好.

此外,另外需要注意的是,因为它不需要您使用相反的命令来撤消之前完成的操作,这意味着任何可能的单向函数[例如散列或加密]都无法通过倒数进行简单的撤消通过回滚到较旧的快照,可以非常简单地撤消命令.

同样如指出的那样,命令模式可能资源密集程度较低,因此我将在特定情况下承认:

  • 存在大的对象状态和/或
  • 没有破坏性的方法和
  • 可以非常简单地使用相互命令来反转所采取的任何行动

命令模式可能更合适[但不一定,它将在很大程度上取决于情况].在其他情况下,我会使用memento模式.

我可能会避免使用这两者的混搭,因为我倾向于关心开发人员,这些开发人员会支持我,并维护我的代码以及我的雇主的道德责任,使这个过程变得简单和便宜可能.我看到这两种模式的混搭很容易变成一个难以维护的老鼠洞,这种老鼠难以维持.


vin*_*ent 8

这里有三种可行的方法.纪念模式(快照),命令模式和状态差异.它们都有优点和缺点.

如果你可以摒弃它,我会选择State Diffing,因为它结合了内存减少,易于实现和可维护性.

请注意,文章中提到的VoxelShop是开源的.所以你可以在这里看一下命令模式的复杂性:https: //github.com/simlu/voxelshop/tree/develop/src/main/java/com/vitco/app/core/data/history

以下摘自文章:

纪念品模式

在此输入图像描述

优点

  • 实施与应用的行动无关.实施后,我们可以添加操作而不必担心破坏历史记录.
  • 快速前进到历史中的预定义位置.当在当前和期望历史位置之间应用的动作在计算上是昂贵的时,这是有趣的.

缺点

  • 与其他方法相比,内存要求可以显着提高.
  • 如果快照很大,加载时间可能会很慢.

命令模式

在此输入图像描述

优点

  • 内存占用很小.我们只需要将更改存储到模型中,如果这些更改很小,那么历史堆栈也很小.

缺点

  • 我们不能直接进入任意位置,而是需要取消应用历史堆栈直到我们到达那里.这可能很耗时.
  • 每个动作及其反转都需要封装在一个对象中.如果你的行动不重要,那么这可能很困难.(反向)动作中的错误实际上很难调试,很容易导致致命的崩溃.即使是简单的行动通常也会涉及很多复杂性.例如,对于3D编辑器,添加到模型的对象需要存储添加的内容,当前选择的颜色,覆盖的内容,镜像模式是否处于活动状态等.
  • 当动作没有简单的反转时,例如模糊图像时,实施和记忆密集可能具有挑战性.

国家差异

在此输入图像描述

优点

  • 实施与应用的行动无关.添加历史记录功能后,我们可以添加操作而不必担心破坏历史记录.
  • 内存要求通常远低于快照方法,并且在许多情况下与命令模式方法相当.但是,这在很大程度上取决于所应用的操作类型.例如,使用命令模式反转图像的颜色应该非常便宜,而状态差异将保存整个图像.相反,在绘制长自由格式线时,如果命令模式方法链接每个像素的历史条目,则它可能会使用更多内存.

缺点/局限

  • 我们不能直接进入任意位置,而是需要取消应用历史堆栈直到我们到达那里.
  • 我们需要计算状态之间的差异.这可能很昂贵.
  • 根据您的数据模型,在模型状态之间实现xor diff可能很难实现.

参考:

https://www.linkedin.com/pulse/solving-history-hard-problem-lukas-siemon

  • @b1nary.atr0phy 最后一些建设性的反馈!我已经调整了答案,如果您能删除您的反对票,我将不胜感激! (2认同)

wom*_*omp 6

经典的做法是遵循命令模式.

您可以使用命令封装执行操作的任何对象,并让它使用Undo()方法执行反向操作.您将所有操作存储在堆栈中,以便通过它们轻松地进行倒带.

  • 这不允许使用无法使用倒数方法“撤消”的诸如散列或数学函数之类的单向函数。因此,对于此方法,应谨慎使用此模式。 (2认同)
  • @Ben通常是命令方法的情况.对于每个命令,您需要存储一些状态来撤消命令.以DeleteSelectedTextCommand为例,其undo命令需要删除的文本.在某些情况下,撤销状态只需要包括整个状态.但对于大多数情况,我认为你总是使用完整状态副本的建议是最实用的解决方案. (2认同)