一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作。

要实现上面的这一功能,最直观的想法就是,我们需要把执行的命令以及相应的参数记录下来,一个命令或者动作,我们可以想象成一个对象,将这些的命令以对象的方式放到一个Stack里面,然后Undo的时候,Pop出来,然后执行该命令即可返回之前的状态。

将命令或者操作抽象为一个对象,使得可以用不同的请求参数对对象进行初始化,使得可以对命令进行排队处理,记录请求,以及执行Undo和Redo操作,这就是命令模式(Command Pattern),命令模式最大的优点就是,他将对象方法的调用和实现分离开

为了说明如何实现Undo和Redo,我们尝试做一个简单的文本格式化的小工具,就是能够进行加粗,倾斜,加下划线,然后支持重做和撤销操作。

首先我们可以定义表示命令执行的抽象类或者接口,这里使用借口ICommand,里面有Execute和UndoExecute两个方法:

public interface ICommand
{
    void Execute();
    void UndoExecute();
}

然后我们的所有命令都实现这一接口:

class BoldCommand : ICommand
{
    private Document document;
    private string previousText;
    public BoldCommand(Document document)
    {
        this.document = document;
        previousText = this.document.Text;
    }

    public void Execute()
    {
        document.BoldSelection();
    }

    public void UndoExecute()
    {
        document.Text = previousText;
    }
}

这个是加粗的命令的接口,参数Document表示编辑的页面,通过构造函数传进来,并且保留处理之前的文本样式。在执行方法Execute中,执行document的BoldSelection方法,在UndoExecute方法中,直接将之前未处理的文字复制给当前的文字状态予以显示。

其他的方法UnderlineCommand,ItalicizeCommand方法和这个类似,这里就不一一显示。

然后,我们需要建立一个管理这些命令的管理类。在这个类中我们两个Stack,commandUndoStack用来来保存所有的可以取消的操作,commandRedoStack用来保存所有重做的操作。方法执行的时候,首先调用ICommand方法的Execute方法,然后将该命令对象Push到UndoStack上,待以后撤销使用:

有了以上数据结构,撤销和重做逻辑就很简单,当用户点击撤销的时候:

  1. 首先检查 commandUndoStack是否为空,如果为空,直接返回,否则继续.
  2. 从commandUndoStack中Pop出最近一次的操作对象ICommand对象
  3. 然后将该命令对象Push到commandRedoStack上保存以便以后重做。
  4. 最后Pop出来的命令对象ICommand的UndoExecute方法实现撤销.

和取消类似,当用户点击重做的时候

  1. 首先检查commandRedoStack是否为空,如果为空,直接返回,否则继续.
  2. 从commandRedoStack中Pop出最近一次的操作对象ICommand对象
  3. 然后将该命令对象Push到commandUndoStack上保存以便以后撤销。
  4. 最后Pop出来的命令对象ICommand的Execute方法实现重做.

整个CommandManager对象实现如下:

class CommandManager
{
    private Stack<ICommand> commandUndoStack = new Stack<ICommand>();
    private Stack<ICommand> commandRedoStack = new Stack<ICommand>();

    public void ExecuteCommand(ICommand cmd)
    {
        cmd.Execute();
        commandUndoStack.Push(cmd);
    }

    public void Undo()
    {
        if (commandUndoStack.Count > 0)
        {
            ICommand cmd = commandUndoStack.Pop();
            cmd.UndoExecute();
            commandRedoStack.Push(cmd);

        }
    }

    public void Redo()
    {
        if (commandRedoStack.Count > 0)
        {
            ICommand cmd = commandRedoStack.Pop();
            cmd.Execute();
            commandUndoStack.Push(cmd);
        }
    }
}

最后,当用户执行某一命令的时候,只需要实例化一个CommandManager管理类,然后将待执行的命令ICommand传进去作为参数执行即可。

private CommandManager commandManager;
private Document document;
public Form1()
{
    InitializeComponent();

    document = new Document(this.textBox1);
    commandManager = new CommandManager();
}

private void btnBold_Click(object sender, EventArgs e)
{
    ICommand boldCommand = new BoldCommand(document);
    commandManager.ExecuteCommand(boldCommand);
}

private void btnItalic_Click(object sender, EventArgs e)
{
    ICommand italicCommand = new ItalicizeCommand(document);
    commandManager.ExecuteCommand(italicCommand);
}

private void btnUnderLine_Click(object sender, EventArgs e)
{
    ICommand underLineCommand = new UnderlineCommand(document);
    commandManager.ExecuteCommand(underLineCommand);
}

private void btnRedo_Click(object sender, EventArgs e)
{
    commandManager.Redo();
}

private void btnUndo_Click(object sender, EventArgs e)
{
    commandManager.Undo();
}

整个代码的UML结构图如下,代码简洁清晰吧。

本文通过实现一个简单的Undo,Redo的功能简单演示了命令模式的实现及原理,希望对大家有所帮助。

从Undo,Redo谈命令模式的更多相关文章

  1. 深入浅出设计模式——命令模式(Command Pattern)

    模式动机 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请 ...

  2. 设计模式(十四):Command命令模式 -- 行为型模式

    1.概述         在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来 ...

  3. 设计模式 ( 十三 ) 命令模式Command(对象行为型)

    设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述         在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...

  4. Head First 设计模式之命令模式(CommandPattern)

    前言: 本章会将封装带入到一个全新的境界,把方法调用封装起来.通过封装方法调用,把运算块包装成形.调用此运算的对象不需要知道事情是如何进行的,只要知道如何使用包装形成的方法来完成它就ok了. 1 现实 ...

  5. HeadFirst设计模式之命令模式

    一. 1.因为是操作经常变化,所以封装操作为command对象.You can do that by introducing “command objects” into your design. A ...

  6. 命令模式(head first 设计模式5)

    一.命令模式定义 命令大家都不会陌生,那么在开始命令模式之前,可以想象一下生活中的命令模式的特点: 如老板命令你完成一个OA项目是一个命令,接着看看其特点: 1.在上面的命令中,命令的执行者肯定是聪明 ...

  7. C#设计模式之11:命令模式

    C#设计模式之11:命令模式 命令模式 命令模式用来解决一些复杂业务逻辑的时候会很有用,比如,你的一个方法中到处充斥着if else 这种结构的时候,用命令模式来解决这种问题就会让事情变得简单很多. ...

  8. 命令模式-实现undo和redo

    这次实验主要是实现多次redo和undo,即程序的撤回和恢复,这里只实现加法的撤回和恢复. 程序的撤回和恢复就是由所使用的软件来记录操作步骤,可以将数据恢复到某个操作状态. 撤回这个指令很常见,Win ...

  9. 设计模式 - 命令模式(command pattern) 撤销(undo) 具体解释

    命令模式(command pattern) 撤销(undo) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 參考命令模式: http://blog.cs ...

随机推荐

  1. Js(DOM) 和Jq 对象的相互转换

    JQuery 对象不能使用DOM对象中的方法,同样,Dom对象 不能使用JQuery 中的方法,但有时候 ,我们不得不使用JQuery的方法或者 Dom对象的方法,该怎么办呢? 下面介绍一下 jq对象 ...

  2. 谈谈iOS中的屏幕方向

    众所周知,iOS中提供了[UIDevice currentDevice].orientation与[UIApplication sharedApplication].statusBarOrientat ...

  3. 【Mybatis架构】 延迟加载

    在上一篇博客中,我们提到过有关于Mybatis输出映射中resultMap能够实现延迟加载的事,然而真的是所有的resultMap都能实现延迟加载还是咋地啊?现在我们就来对那一句话做一下阐述和实例说明 ...

  4. 盘点销售一体机 打印POS一体的设备。 打印,盘点,销售PDA(手持终端)+移动销售POS软件

    一.产品介绍 本产品为一个PDA(手持终端)+移动销售POS管理软件组合.可以实现功能如下: 可以实现移动销售(销售退货).盘点.配送.移库.打印小票的功能. 销售时,可以选择客户.业务员.库房,并且 ...

  5. 好文分享_java堆栈的区别

    Java Heap Memory Heap memory is used by java runtime to allocate memory to Objects and JRE classes. ...

  6. css3clock.js - 一个用CSS3与纯js实现的简单时钟

    前言 项目代码明细可以查看我Github上的源码:https://github.com/nelsonkuang/css3clock.js 实现思路 主要是用CSS3控制时针.分针和秒针旋转:时针每12 ...

  7. C语言中函数声明实现的位置

    在学习C语言的时候我遇到了这么个事情,因为之前先学习的C#,在C#编译器中,函数的声明位置不会影响编译的结果,但是在C语言中却发生了错误 先看一段代码: #include <stdio.h> ...

  8. 遍历datatable的方法

    +方法一:DataTable dt = dataSet.Tables[0];for(int i = 0 ; i < dt.Rows.Count ; i++){string strName = d ...

  9. Android之获取数据库路径

    如果数据库名为:“ifoData.db”,则它的路径求法为 String pathDatabase=Mcontext.getDatabasePath("ifoData.db").g ...

  10. 初探javascript

    javascript是一种在网络广泛应用的脚本语言,虽然名字与java相近,但其实两者并没有直接的关系,脚本语言是一种为了便于操作和拓展功能而开发出来的解释性语言,不同于传统的编程语言,脚本语言不需要 ...