c#中的命令模式和复杂操作

本文关键字:复杂 操作 模式 命令 | 更新日期: 2023-09-27 18:18:43

我正在用c#写一个程序,需要支持撤销/重做。为此,我选定了命令模式;在tldr中,每个操作文档状态的操作都必须由一个Command对象执行,该对象知道文档的先前状态以及需要进行的更改,并且能够执行/撤销自己。

对于简单的操作它工作得很好,但是我现在有一个操作,它会同时影响文档的几个部分。同样地,Command对象必须足够聪明,知道它需要保留的所有旧状态,以防需要撤消。

问题是,如果有人试图直接调用接口,那么使用公共接口暴露所有状态有可能被滥用,这可能导致状态损坏。我的直觉告诉我,做这件事最OO的方式是公开专门的Command类——而不是允许你直接操纵文档的状态,你所能做的就是让文档创建一个Command对象,这个对象可以访问它的内部状态,并保证知道足够的信息来正确支持撤销/重做。

不幸的是,c#不支持友元的概念,所以我不能创建一个可以访问文档内部的Command类。是否有一种方法可以将文档类的私有成员公开给另一个类,或者是否有其他方法可以在不公开大量文档内部的情况下完成我所需要的工作?

c#中的命令模式和复杂操作

这取决于,如果你正在部署一个库,你的文档可以声明'内部'方法来与它的内部状态交互,这些方法将被你的命令类使用,内部方法仅限于它们被编译的程序集。

或者你可以为你的Document嵌套一个私有类,这样就允许它访问Document的内部状态并向它公开一个公共接口,然后你的Document将创建一个隐藏在该接口下的命令类

首先,c#的internal关键字声明了"友元"可访问性,这允许从整个程序集中进行公共访问。

其次,"朋友"的可访问性可以扩展到第二个具有汇编属性InternalsVisibleTo的程序集,这样您就可以为您的命令创建第二个项目,但是文档的内部将保持内部。

或者,如果您的命令对象嵌套在文档类中,那么它们将可以访问其所有私有成员。

最后,复杂的命令还可以在进行更改之前简单地克隆文档。这是一个简单的解决方案,尽管不是很优化。

你总是可以访问字段和属性,私有或非,通过反射(Type.GetField(string, BindingFlags.Private) &朋友).

也许在类(或字段/属性)上使用自定义属性来自动为每个命令捕获足够的状态?

您可以使用两个虚拟命令来标记多步骤操作的开始和结束,而不是使用命令在文档的不同位置进行更改。我们称它们为BeginCommand和EndCommand。首先,在撤销堆栈上推入BeginCommand,然后将不同的步骤作为单个命令执行,每个命令仅在文档的单个位置进行更改。当然,你也会把它们推到撤消堆栈上。最后,在undo-stack上按EndCommand。

撤销时,检查从撤销堆栈弹出的命令是否为结束命令。如果是,你继续撤销,直到到达BeginCommand。

这将多步骤命令转换为宏命令,将工作委托给其他命令。这个宏命令本身不会被压入undo堆栈。