博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用Command模式简单的实现Undo&Redo功能
阅读量:5147 次
发布时间:2019-06-13

本文共 1720 字,大约阅读时间需要 5 分钟。

许多GUI程序中提供一个"撤销&重做"的功能,这个功能对用户来说非常友好;本文就简单的介绍一下如何用C#实现该功能。

实现Undo&Redo功能的基本模型是带撤销功能的命令模式,它将每步操作保存为一个命令对象,如下所示:

    interface Icommand

    {
        void Do();
        void Undo();
    }

其中Do函数执行功能,Undo函数回滚功能。这样就把命令给实体化了,只要将命令对象给保存下来,需要撤销时执行Undo函数,重做时执行Do函数即可。

有了这个基本思路后,下面就是实现细节了:

  1. 申请两个Stack来保存命令对象:UndoStack和RedoStack
  2. 执行命令时,将命令序列化为Command对象,执行Do方法,存入UndoStack,清空RedoStack
  3. 撤销命令时,从UndoStack中取出命令,执行Undo方法,存入RedoStack
  4. 重做命令时,从RedoStack中取出命令,执行Do方法,存入UndoStack

一个简单的实现如下:

    class CommandManager

    {
        Stack<Command> redoStack = new Stack<Command>();
        Stack<Command> undoStack = new Stack<Command>();
        public void AddCommand(Action doCmd, Action undoCmd)
        {
            var cmd = new Command(doCmd, undoCmd);
            cmd.Do();
            undoStack.Push(cmd);
            redoStack.Clear();
        }
        public bool Undo()
        {
            if (undoStack.Count == 0)
                return false;
            var cmd = undoStack.Pop();
            redoStack.Push(cmd);
            cmd.Undo();
            return true;
        }
        public bool Redo()
        {
            if (redoStack.Count == 0)
                return false;
            var cmd = redoStack.Pop();
            undoStack.Push(cmd);
            cmd.Do();
            return true;
        }
        class Command
        {
            public Action Do { get; private set; }
            public Action Undo { get; private set; }
            public Command(Action doCmd, Action undoCmd)
            {
                this.Do = doCmd;
                this.Undo = undoCmd;
            }
        }
    }

用C#实现起来还是非常简洁的,就几十行代码。

遗留问题:命令对象何时释放

前面的实现虽然非常简单,但存在一个遗留问题:每一个命令对象都保存在UndoStack中了,这样随着程序的执行,UndoStack中记录的命令越来越多,占用内存得不到释放。对于这个问题,一般有如下几种策略:

  1. 不释放命令对象。一般需要Undo&Redo功能都是些GUI程序,这些程序大多不会持续运行,并且对内存的占用也没有太大限制,命令对象一般也不会占用多少内存。保存所有命令对象不会对程序造成什么影响。
  2. 命令堆栈维持固定的长度:当命令堆栈的长度超过阈值的时候,删除最开始压入的命令。这种策略用得最多,但这样带来的问题就是无法实现无限Undo。
  3. 将命令堆栈保存到文件:将命令序列化保存到文件,需要使用时从文件中还原。这种方式可以实现无限Undo,但序列化命令往往是件比较麻烦的事情,反序列化时也要消耗时间。
  4. 综合2,3两种方案:内存中保持固定长度的命令对象,超过阈值的保存到文件。这种方式能有效解决反序列化的耗时问题,也能实现无限Undo。但实现起来也最为麻烦。

基于篇幅所限,本文就不进一步讨论和实现了。

转载于:https://www.cnblogs.com/TianFang/archive/2013/05/19/3086820.html

你可能感兴趣的文章
基于wxPython的python代码统计工具
查看>>
淘宝JAVA中间件Diamond详解(一)---简介&快速使用
查看>>
一种简单的数据库性能测试方法
查看>>
如何给JavaScript文件传递参数
查看>>
Hadoop HBase概念学习系列之HBase里的宽表设计概念(表设计)(二十七)
查看>>
Kettle学习系列之Kettle能做什么?(三)
查看>>
电脑没有安装iis,但是安装了.NET环境,如何调试网站发布的程序
查看>>
【Mac + GitHub】之在另一台Mac电脑上下载GitHub的SSH链接报错
查看>>
Day03:Selenium,BeautifulSoup4
查看>>
awk变量
查看>>
mysql_对于DQL 的简单举例
查看>>
postgis几何操作函数集
查看>>
35. Search Insert Position(C++)
查看>>
[毕业生的商业软件开发之路]C#异常处理
查看>>
一些php文件函数
查看>>
std::min error C2059: 语法错误:“::” 的解决方法
查看>>
Opencv保存摄像头视频&&各种编码器下视频文件占用空间对比
查看>>
「图形学」直线扫描——Bresenham算法改进了中点Bresenham算法?
查看>>
jQuery 给div绑定单击事件
查看>>
Exceptionless 生产部署笔记
查看>>