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

第一次写关于设计模式的随笔,最近在使用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);
}

恢复操作:

private void button2_Click(object sender, EventArgs e) //恢复
         {
             if (redoStack.Count == 0)
                 return;

             flag = false;
             Command com = redoStack.Pop();
             com.execute();

             undoStack.Push(com);
         }

我将两个操作分别放到两个button的操作里面,一个用于撤销,另外一个用于恢复。由于我们在进行撤销和恢复时,在TextChangedCommand中是对TextBox控件进行赋值,因此也会触发textBox1控件的Text属性变化的回调函数,我这这里使用了一个标志flag,如果是在撤销和恢复的动作中,则不做Command对象的压栈动作。到这里,已经完全可以实现了TextBox的撤销和恢复功能,当然在我自己的项目中,出了TextBox控件为,还有ComboBox、DateTimePicker、CheckBox控件都实现了撤销和恢复功能,但是ComboBox控件的回调函数需要使用SelectIndexChanged作为回调,不要使用SelectTextChanged,因为当Index变化时也会触发Text属性的变化。

这里简单介绍一下在撤销恢复时,不触发textBox1的Text的属性回调函数的另外一种方法。我在这里将其称之为原子操作。可以将textBox1的回到函数textBox1_TextChanged作为一个参数,传入到Command对象中。在对TextBox的Text属性变化前先做这个动作:ctrl.TextChanged -=动作,赋值完成后,再做Ctrl.TextChanged +=动作。下面用一个函数简单模拟一下:

void setTextBox(TextBox ctrl, String str,  EventHandler eventhandler)

{

  ctrl.TextChanged -= eventhandler;

ctrl.Text = str;

ctrl.TextChanged += eventhandler;

}

附上源码:http://files.cnblogs.com/files/youyipin/test_Charp.zip

时间: 2024-08-07 08:30:57

C#使用命令模式实现撤销和恢复功能的相关文章

《JAVA与模式》之命令模式

命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 命令模式的结构 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象. 每一个命令都是一个操作:请求的一方发出请求要求执行一个操作:接收的一方收到请求,并执行操作.命令模式允许请求的一方和接收的一方独立开来,

设计模式之禅之设计模式-命令模式

一:命令模式的定义        --->命令模式是一个高内聚的模式        --->将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能.        --->命令模式的角色                ● Receive接收者角色==>该角色就是干活的角色,命令传递到这里是应该被执行的                ● Command命令角色==>需要执行的所有命令都在这里声明         

【设计模式】命令模式

原文链接http://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html 原文先放在这里,后续会做自己的补充.     命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式.     命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 命令模式的结构 命令模式是对命令的封装.命令模

java设计模式之命令模式以及在java中作用

命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 命令模式的结构 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象. 每一个命令都是一个操作:请求的一方发出请求要求执行一个操作:接收的一方收到请求,并执行操作.命令模式允许请求的一方和接收的一方独立开来,

设计模式之命令模式---Command Pattern

模式的定义 命令模式是一个高内聚的模式,定义如下: Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录日志,可以提供命令的撤销和恢复功能. 模式的使用场景 只要

设计模式(14)--Command(命令模式)--行为型

作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义:   命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式.   命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 2.模式特点: 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序

15.设计模式_命令模式

一.前言 之前一直在忙于工作上的事情,关于设计模式系列一直没更新,最近项目中发现,对于设计模式的了解是必不可少的,当然对于设计模式的应用那更是重要,可以说是否懂得应用设计模式在项目中是衡量一个程序员的技术水平,因为对于一个功能的实现,高级工程师和初级工程师一样都会实现,但是区别在于它们实现功能的可扩展和可维护性,也就是代码的是否"优美".可读.但是,要更好地应用,首先就必须了解各种设计模式和其应用场景,所以我还是希望继续完成设计模式这个系列,希望通过这种总结的方式来加深自己设计模式的理

命令模式与策略模式之己见

以前项目写过关于TR069协议报文处理的代码(主要是基于SOAP协议发送一些远程命令并处理响应),在设计的时候,想的是应用策略模式对报文进行解析处理, 下图是主要代码结构(和策略模式很像) 代码类似于: /** * 1.需要解析的XML */ String xml = "<xml>...</xml>"; /** * 2.获取xml类型 */ MessageType type = SoapMessageFactory.getRpcType(xml); /** *

(9)命令模式

定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 类型:行为类模式 类图: 命令模式的结构 顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构: l  Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令. l  ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现. l  Client类:最终的客