【问题标题】:Undo/Redo with immutable objects使用不可变对象撤消/重做
【发布时间】:2010-12-21 05:49:56
【问题描述】:

我在article 中阅读了以下内容

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

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

【问题讨论】:

    标签: language-agnostic undo-redo immutability


    【解决方案1】:

    不可变对象将保存系统的整个状态,因此在这种情况下,您将拥有包含原始收件箱的对象 A,然后是包含已删除十封电子邮件的收件箱的对象 B,并且(实际上) 一个从 B 指向 A 的指针,表示如果您执行一次“撤消”操作,那么您将停止使用 B 作为系统的状态并开始使用 A。

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

    如果您想保留十级撤消,您可以通过仅保留两个不可变对象(一个是当前对象,一个来自十个“撤消”之前的对象)以及在这两个对象之间应用的命令列表来潜在地节省内存他们。

    要进行“撤消”,您需要重新执行除最后一个 Command 对象之外的所有对象,将其用作新的当前对象,然后擦除最后一个 Command(或将其保存为“Redo”对象)。每次执行新操作时,都会更新当前对象,将关联的命令添加到列表中,然后(如果列表长度超过十个命令)从撤消列表的开头对对象执行第一个命令并丢弃列表中的第一个命令。

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

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

    【讨论】:

    • “Gmail 收件箱太大,无法使用这种技术”。伊什。如果实际消息是单独的对象,并且两个不同的收件箱对象引用相同的消息,那么收件箱只是一个列表,您可能可以这样做。不可变对象的另一个好处是对象可以像这样安全地共享资源。对于可变对象,您可能不得不担心对一个对象的更改会错误地显示在另一个对象中。
    • 这是一个很好的观点。我确实注意到,对于 GMail 帐户,适当的不可变对象将是标签列表以及每个标签的相关电子邮件对象列表;这比整个存档要小得多,但比收件箱消息列表要大得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 2012-09-05
    • 2017-03-19
    相关资源
    最近更新 更多