使用不可变对象撤消/重做

Omn*_*ent 6 language-agnostic immutability undo-redo

我在一篇文章中阅读了以下内容

不可变对象对于实现某些常见习语(例如撤销/重做和可中止事务)特别方便.以撤消为例.实现undo的一种常用技术是保持一堆对象以某种方式知道如何反向运行每个命令(所谓的"命令模式").但是,弄清楚如何反向运行命令可能会很棘手.一种更简单的技术是维护一堆不可变对象,表示连续命令之间的系统状态.然后,要撤消命令,只需恢复到先前的系统状态(并可能将当前状态存储在重做堆栈上).

但是,文章没有展示如何使用不可变对象来实现"撤销"操作的一个很好的实际例子.例如...从Gmail收件箱中删除10封电子邮件.一旦你这样做,它有一个撤消选项.在这方面,不可变对象如何帮助?

jpr*_*ete 6

不可变对象将保持系统的整个状态,因此在这种情况下,您将拥有包含原始收件箱的对象A,然后包含已删除10封电子邮件的收件箱的对象B,并且(实际上)指针从B返回到A表示,如果您执行一个"撤消",则停止使用B作为系统状态并开始使用A代替.

但是,Gmail收件箱太大而无法使用此技术.您可以在实际存储在相当少量内存中的文档中使用它,这样您就可以将其中的许多内容保留在多级撤消中.

如果你想保持十级撤消,你可以通过只保留两个不可变对象来保存内存 - 一个是当前的,一个来自10个"undos"前 - 以及它们之间应用的命令列表.

要执行"撤消",您将重新执行除最后一个Command对象之外的所有对象,将其用作新的当前对象,并擦除最后一个Command(或将其另存为"Redo"对象).每次执行新操作时,都会更新当前对象,将关联的命令添加到列表中,然后(如果列表长度超过十个命令),则从撤消列表的开头执行对象上的第一个命令并扔掉名单上的第一个命令.

您还可以执行各种其他检查点系统,包括系统的可变数量的完整表示以及它们之间的可变数量的命令.但它越来越远离你引用的原始想法,变得越来越像一个典型的可变系统.但是,它确实避免了使命令始终可逆的问题; 您只需将命令应用于对象前进而不是反向.

SVN和其他版本控制系统实际上是基于磁盘或网络的撤销和重做形式.

  • "Gmail收件箱太大,无法使用此技术".伊什.如果实际消息是单独的对象,并且两个不同的收件箱对象引用相同的消息,则收件箱只是一个列表,您可能可以这样做.关于不可变对象的另一个好处是对象可以安全地共享这样的资源.对于可变对象,您可能不得不担心对另一个错误地显示的更改. (3认同)