C#使用命令模式实现撤销和恢复功能

时间:2022-01-18 19:33:06

第一次写关于设计模式的随笔,最近在使用C#做一个WinForm的项目,其中要求需要支持撤销和恢复功能,想到了以前看过Command模式支持撤销和恢复操作,就在项目中使用了。对命令模式理解的不够深,各位看客请指正。

Gof23种设计模式中的Command模式,其意图是这么描述的“将一个请求封装为一个对象,从而是你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作”;另外个人的理解就是可以将调用者和接受者解耦出来。下图为Comand的类图:

。首先理解将调用者和接受者解耦出来:Invoker中只需要持有一个Command接口的引用即可,但是具体是其哪一个子类,,Invoker并不知道。而真正执行动作的接受者,Invoker并不用知道,这样就讲调用者和接收者解耦出来。另外理解实现撤销和恢复操作。程序需要将所有操作过的命令对象存储起来,存储的每一个命令对象保存了其当前状态和上一个状态,因此可用来做撤销和恢复操作。下面用代码进行说明,本文使用C#语言实现的。

首先,我们定义一个Command的接口。定义如下:

interface Command { void execute(); void undo(); }

其中仅仅声明了两个函数,其如果在c++的实现的话,使用纯虚类。

我们要实现的具体功能为,可以为一个TextBox控件实先撤销和恢复功能。因此,我们实先一个TextChangedCommand的类,定义如下:

1 public class TextChangedCommand: Command 2 { 3 private TextBox ctrl; 4 private String newStr; 5 private String oldStr; 6 7 public TextChangedCommand(TextBox ctrl, String newStr, String oldStr) 8 { 9 this.ctrl = ctrl; 10 this.newStr = newStr; 11 this.oldStr = oldStr; 12 } 13 14 public void execute() 15 { 16 this.ctrl.Text = newStr; 17 this.ctrl.SelectionStart = this.ctrl.Text.Length; 18 } 19 20 public void undo() 21 { 22 this.ctrl.Text = oldStr; 23 this.ctrl.SelectionStart = this.ctrl.Text.Length; 24 } 25 }

其中有三个成员变量,分别为TextBox类型的ctrl,和String类型的newStr、oldStr。这里的ctrl就可以理解为类图中的Invoker,而newStr和oldStr即为当前状态和上一个状态。

由于我们需要实现其撤销和恢复功能,因此这里创建两个栈对象,分别用来存储已经执行了的命令对象和撤销后的命令对象。即在撤销动作中,从栈中弹出一个Command对象,然后执行对象的undo操作,执行完成后将其存储到恢复的栈中,在点击恢复时,用从恢复栈弹出一个Command对象,执行其execute动作。两个栈的定义如下:

Stack<Command> undoStack = new Stack<Command>(); Stack<Command> redoStack = new Stack<Command>();

下面实现类图中的Client部分,其主要功能是创建一个具体的Command对象,同时为为其设置接收者。对于一个TextBox来说,在其Text熟悉变化的时候,将其上一个状态和当前状态保存下来。具体试下如下:

private void textBox1_TextChanged(object sender, EventArgs e) {
if(flag){ TextChangedCommand com
= new TextChangedCommand((TextBox)textBox1, ((TextBox)textBox1).Text, oldStr); undoStack.Push(com); oldStr = ((TextBox)textBox1).Text;
} }

这个函数是控件textBox1的Text属性变化时的回调函数。在其中创建了Command基类的实力,并将其存到栈对象undoStack中。下面来做恢复和撤销操作的代码:

撤销操作:

private void button1_Click(object sender, EventArgs e) //撤销 { if (undoStack.Count == 0) return;     
flag = false;
Command com
= undoStack.Pop(); com.undo(); redoStack.Push(com); }

恢复操作:

相关文章