zoukankan      html  css  js  c++  java
  • 用Command模式简单的实现Undo&Redo功能

    许多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。但实现起来也最为麻烦。

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

  • 相关阅读:
    build.xml介绍
    assetbundle和ScriptableObject的使用
    unity 错误汇总
    【unity基础系列】编辑器类使用例子
    texturepacker使用心得
    vs特殊的快捷方式
    【unity基础系列】1、unity Texture Type设置为Advanced时纹理的格式列表
    嵌套prefabs的使用
    unity基础知识笔记一(快捷方式、基础概念)
    关于游戏研发一些常用的知识记录
  • 原文地址:https://www.cnblogs.com/TianFang/p/3086820.html
Copyright © 2011-2022 走看看