我正在用C#编写一个需要支持undo / redo的程序。为此,我决定使用Command模式; tldr,每个操作文档状态的操作必须由一个Command对象执行,该对象知道文档的先前状态以及需要进行的更改,并且能够自行执行/撤消。
它适用于简单的操作,但我现在有一个操作同时影响文档的几个部分。同样,Command对象必须足够智能,以便在需要撤消时知道它需要保留的所有旧状态。
问题是暴露所有使用公共接口的状态如果有人试图直接调用接口,可能会导致滥用,这可能导致状态损坏。我的insticts告诉我,这样做的最多OO方式是公开专门的Command类 - 而不是让你直接操作文档的状态,你只能要求文档创建一个可以访问它的Command对象。内部状态,并保证足够了解正确支持撤消/重做。
不幸的是,C#不支持朋友的概念,所以我无法创建一个可以访问文档内部的Command类。有没有办法将文档类的私有成员公开给另一个类,还是有其他方法可以做我需要的东西而不必暴露大量的文档内部结构?
答案 0 :(得分:2)
这取决于,如果您正在部署库,您的Document可以声明“内部”方法与其内部状态进行交互,这些方法将由Command类使用,内部方法仅限于它们被编译的程序集。
或者您可以将私有类嵌套到Document中,这样就可以访问Document的内部状态并向其公开一个公共接口,然后Document会创建一个由该接口隐藏的命令类。
答案 1 :(得分:2)
首先,C#具有internal
关键字,声明“朋友”可访问性,允许从整个程序集内进行公共访问。
其次,具有汇编属性InternalsVisibleTo
的“朋友”辅助功能can be extended to a second assembly,以便您可以为命令创建第二个项目,但内部该文件将保留在内部。
或者,如果您的命令对象嵌套在文档类中,那么它们将可以访问其所有私有成员。
最后,复杂的命令也可以在进行更改之前简单地克隆文档。这是一个简单的解决方案,虽然不是很优化。
答案 2 :(得分:0)
您始终可以通过反映(Type.GetField(string, BindingFlags.Private)
和朋友)访问私有与否的字段和属性。
可能在类(或字段/属性)上使用自定义属性来自动获取每个命令的足够状态的过程?
答案 3 :(得分:0)
您可以使用两个虚拟命令来标记多步操作的开始和结束,而不是让命令在文档的不同位置执行更改。让我们称他们为BeginCommand和EndCommand。首先,在撤销堆栈上推送BeginCommand,然后将单个命令执行不同的步骤,每个命令仅在文档的单个位置执行更改。当然,你也可以在撤销堆栈上推送它们。最后,在End-stack上推送EndCommand。
撤消时,检查从撤消堆栈弹出的命令是否为EndCommand。如果是,则继续撤消,直到到达BeginCommand。
这会将多步命令转换为宏命令,将工作委托给其他命令。此宏命令本身不会在撤消堆栈上推送。