我有一个根据经典Model-View-Controller pattern设计的C ++应用程序。该模型通过外部源通过控制器接口通过Command pattern进行修改。命令由Action对象(及其派生词)表示。
现在我希望能够撤消修改,但我的问题是我的控制器中没有getter,只有setter。这似乎很合乎逻辑,因为没有理由有人能够通过控制器获取有关模型的信息。因此,我不能让我的Action对象存储模型的状态,因为它们无法访问它。
如何解决这个问题?我想保持我的应用程序尽可能扩展,我不太确定哪个选项最适合。到目前为止我的方法是:
也许有更好的方法来做到这一点?现在,作为最佳选择似乎是2,子选项1(使用子选项2,我很可能存储比撤消一个操作所需的更多状态)。
注意:我知道还有其他关于如何实施撤消操作的问题。但是,我发现的唯一答案给出了使用Command或Memento模式的建议。我知道这很可能是要走的路。我要求的是如何将其整合为干净利落的。可以在MVC设计中扩展。
[编辑] 我对Memento模式不喜欢的是它迫使我存储一个完整的状态。假设我的模型是1000x1000矩阵,我的命令是ChangeOneValueAtLocation。为了能够撤消其更改,ChangeOneValueAtLocation对象只需要存储它正在更改的位置的先前值,但Memento似乎无法做到这一点。我的模型越大,这个问题就越大。
[编辑2] 在本应用程序的特定情况下,我对Memento的另一个问题是:对于Command对象可以在Model上执行的每个方法,都有一个方法完全相反(或者可以容易被哄骗这样做)。这就是为什么我觉得必须存储整个状态是浪费的,应该没有必要,恢复单个命令是非常简单的,唯一的问题是让数据能够做到。
此外,我不需要能够撤消特定命令,只能撤消历史堆栈中最顶层的命令。
答案 0 :(得分:2)
我真的建议在你的Controller中构建撤销树
将其构建到模型中会让您遇到麻烦:
控制器是'动作调度员',所以它必须说
然后撤消
此外,使用高级操作撤消工作(请参阅复合模式或命令模式)
答案 1 :(得分:2)
我也支持包含撤消支持的模型层。在模型方面有很多方法可以解决这个问题。第一个也是最明显的是模型本身用“标签”记住变化的历史,但这对于所有模型类来说可能很难同步。
另一个选择是创建一个具有“事务”概念的历史管理器,使其生成撤销点,拍摄模型的快照,或开始记录更改(减少内存使用量),或记录导致模型更改的命令等。模型通知管理员更改,最后完成事务(或不完成,因为下一个事务开始可能是前一个事务的结束)。一旦你添加了回滚到某一点的能力,工作就会完成。通过在这个管理器类中使事情稍微复杂一点,你可以创建一个撤销树(就像emacs中的那个),所以它也是一种非常灵活的方法来处理它。
但是,上述解决方案并不完全在模型层中。它是由模型和控制器驱动的支持类。如果删除事务概念,那么它完全是模型驱动的,但实现撤消操作的概念可能有些棘手。如果将其更改为命令代理,则它是控制器使用的唯一实体,并且显然是一个模型。在这一点上选择一种方法而不是另一种方法的设计太粗糙了,但我倾向于“交易”模型。实施起来感觉很容易。
答案 2 :(得分:0)
在模型中构建撤消功能。让模型保留一个命令列表。当视图将撤消信号传递给模型时,以相反的顺序运行命令。