撤消MVC设计中的功能

时间:2011-04-17 18:17:25

标签: c++ design-patterns

我有一个根据经典Model-View-Controller pattern设计的C ++应用程序。该模型通过外部源通过控制器接口通过Command pattern进行修改。命令由Action对象(及其派生词)表示。

现在我希望能够撤消修改,但我的问题是我的控制器中没有getter,只有setter。这似乎很合乎逻辑,因为没有理由有人能够通过控制器获取有关模型的信息。因此,我不能让我的Action对象存储模型的状态,因为它们无法访问它。

如何解决这个问题?我想保持我的应用程序尽可能扩展,我不太确定哪个选项最适合。到目前为止我的方法是:

  1. 将getter方法放入控制器中。这似乎违背了MVC模式。
  2. 为Action指定一个View。该行动可以:
    1. 使用单个getter获取要修改的模型的特定元素的状态。
    2. 使用Viewer实现的Memento方法。
  3. 也许有更好的方法来做到这一点?现在,作为最佳选择似乎是2,子选项1(使用子选项2,我很可能存储比撤消一个操作所需的更多状态)。

    注意:我知道还有其他关于如何实施撤消操作的问题。但是,我发现的唯一答案给出了使用Command或Memento模式的建议。我知道这很可能是要走的路。我要求的是如何将其整合为干净利落的。可以在MVC设计中扩展。

    [编辑] 我对Memento模式不喜欢的是它迫使我存储一个完整的状态。假设我的模型是1000x1000矩阵,我的命令是ChangeOneValueAtLocation。为了能够撤消其更改,ChangeOneValueAtLocation对象只需要存储它正在更改的位置的先前值,但Memento似乎无法做到这一点。我的模型越大,这个问题就越大。

    [编辑2] 在本应用程序的特定情况下,我对Memento的另一个问题是:对于Command对象可以在Model上执行的每个方法,都有一个方法完全相反(或者可以容易被哄骗这样做)。这就是为什么我觉得必须存储整个状态是浪费的,应该没有必要,恢复单个命令是非常简单的,唯一的问题是让数据能够做到。

    此外,我不需要能够撤消特定命令,只能撤消历史堆栈中最顶层的命令。

3 个答案:

答案 0 :(得分:2)

我真的建议在你的Controller中构建撤销树

将其构建到模型中会让您遇到麻烦:

  • “模型”通常是每个视图的碎片(每个视图都有自己的部分模型)
  • 这将导致非原子撤销(撤消操作的部分,因为视图不知道其他东西(模型)必须撤消等)。

控制器是'动作调度员',所以它必须说

  1. 克隆状态(所有模型)快照
  2. 参考快照
  3. 向历史记录添加操作
  4. run action
  5. 然后撤消

    1. 关闭历史堆栈的弹出操作(可选择推送到'future'堆栈)
    2. 恢复快照
    3. 显示视图
    4. 此外,使用高级操作撤消工作(请参阅复合模式或命令模式)

答案 1 :(得分:2)

我也支持包含撤消支持的模型层。在模型方面有很多方法可以解决这个问题。第一个也是最明显的是模型本身用“标签”记住变化的历史,但这对于所有模型类来说可能很难同步。

另一个选择是创建一个具有“事务”概念的历史管理器,使其生成撤销点,拍摄模型的快照,或开始记录更改(减少内存使用量),或记录导致模型更改的命令等。模型通知管理员更改,最后完成事务(或不完成,因为下一个事务开始可能是前一个事务的结束)。一旦你添加了回滚到某一点的能力,工作就会完成。通过在这个管理器类中使事情稍微复杂一点,你可以创建一个撤销树(就像emacs中的那个),所以它也是一种非常灵活的方法来处理它。

但是,上述解决方案并不完全在模型层中。它是由模型和控制器驱动的支持类。如果删除事务概念,那么它完全是模型驱动的,但实现撤消操作的概念可能有些棘手。如果将其更改为命令代理,则它是控制器使用的唯一实体,并且显然是一个模型。在这一点上选择一种方法而不是另一种方法的设计太粗糙了,但我倾向于“交易”模型。实施起来感觉很容易。

答案 2 :(得分:0)

在模型中构建撤消功能。让模型保留一个命令列表。当视图将撤消信号传递给模型时,以相反的顺序运行命令。