• 文本编辑器中,如何设计 撤销/重复栈


              文本编辑器中,如何设计 撤销/重复栈

                                                      电子科技大学软件学院03级02班 周银辉

            在文本编辑中经常使用到“撤销”操作(Ctrl-Z),以及一个与之相应的“重复”操作(Ctrl-Y),各种不同的编辑操作都可以使用这两个操作来撤销或重复,那么如此多的编辑操作是如何被设计到一起而使得一个“撤销”按钮可以撤销各种不同的操作呢?

    关键在于两点:栈 和 多态。

    如何设计:
           很容易想到使用栈来保持那些已经被执行的操作,当要撤销上一步操作时,只需要取出栈顶元素并执行该元素所对应的操作便可。
         接踵而至的一个问题是: 栈中元素类型是什么? 由于我们需要保存各种不同的文本编辑操作,要将它们统一地保存在一起,很自然地,我们应该让这些操作有着统一的父类, 我们栈中元素的类型是该父类类型就可以了.
         我们这里设计了一个接口,所有的可撤销/重复的操作都应该继承该接口:

        
    /// <summary>
        
    /// 可撤销重复操作接口。
        
    /// 所有的可重复可撤销的操作都应该继承这个接口。
        
    /// </summary>
        interface IUndoableOperate
        {
            
    void Undo();
            
    void Redo();
            
    void Execute();
        }

           比如我们有一个操作Operate1,它继承了IUndoableOperate接口

        
    /// <summary>
        
    /// 操作1
        
    /// </summary>
        class Operate1 : IUndoableOperate
        {
            
    #region IUndoableOperate 成员

            
    /// <summary>
            
    /// 撤销该操作时执行
            
    /// </summary>
            public void Undo()
            {
                Console.WriteLine(
    "undo operate1");
            }

            
    /// <summary>
            
    /// 重复该操作时执行
            
    /// </summary>
            public void Redo()
            {
                Console.WriteLine(
    "do operate1");
            }

            
    /// <summary>
            
    /// 执行操作1
            
    /// </summary>
            public void Execute()
            {
                
    this.Redo();
            }

            
    #endregion

        }

           其它任何与Operate1类似的操作都可以放到撤销栈中,以便以后撤销。

           栈中元素都是IUndoableOperate类型,那么当我们取出栈顶元素并调用其Execute()函数时,其能被正确的执行吗?答案是肯定的,这利用了多态。

           现在我们可以设计一个管理器来对栈进行管理,它将记录那些被执行或被撤销的操作,并提供方法允许你对已经执行过的操作进行撤销、已经撤销的操作进行重复。

        
    /// <summary>
        
    /// 撤销重复操作管理器
        
    /// </summary>

        class UndoStackManager
        
    {
            
    /// <summary>
            
    /// 撤销栈
            
    /// </summary>

            Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
            
    /// <summary>
            
    /// 重复栈
            
    /// </summary>

            Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();


            
    public void ClearStack()
            
    {
                
    this.un_stack.Clear();
                
    this.re_stack.Clear();
            }


            
    /// <summary>
            
    /// 获取一个值,指示是否有可撤销的操作
            
    /// </summary>

            public bool CanUndo
            
    {
                
    get
                
    {
                    
    return un_stack.Count != 0;
                }

            }


            
    /// <summary>
            
    /// 获取一个值,指示是否有可重复的操作
            
    /// </summary>

            public bool CanRedo
            
    {
                
    get
                
    {
                    
    return re_stack.Count != 0;
                }

            }


            
    /// <summary>
            
    /// 撤销上一操作
            
    /// </summary>

            public void Undo()
            
    {
                
    if (this.CanUndo)
                
    {
                    IUndoableOperate op 
    = un_stack.Pop();
                    op.Undo();
                    re_stack.Push(op);
                }

            }


            
    /// <summary>
            
    /// 重复被撤销的操作
            
    /// </summary>

            public void Redo()
            
    {
                
    if (this.CanRedo)
                
    {
                    IUndoableOperate op 
    = re_stack.Pop();
                    op.Redo();
                    un_stack.Push(op);
                }

            }



            
    /// <summary>
            
    /// 将某一操作存放到撤销栈中
            
    /// </summary>
            
    /// <param name="op"></param>

            public void PushToUndoStack(IUndoableOperate op)
            
    {
                
    this.un_stack.Push(op);
                
    this.re_stack.Clear();
            }

        }

         以下是完整的示例代码:
    完整的示例代码

    示例代码的执行结果是:
    do operate1
    do operate2
    undo operate2
    undo operate1
    do operate1
    do operate2
  • 相关阅读:
    php使用redis的有序集合zset实现延迟队列
    php使用redis的几种常见方式和用法
    redis缓存雪崩,缓存穿透,缓存击穿的解决方法
    php操作redis数据库方法总结
    mysql 悲观锁与乐观锁的理解
    OAuth2.0 协议的理解
    windows下的mongodb安装与配置
    node.js中对 redis 的安装和基本操作
    node.js中对 mysql 进行增删改查等操作和async,await处理
    node.js中 koa 框架的基本使用方法
  • 原文地址:https://www.cnblogs.com/zhouyinhui/p/520813.html
Copyright © 2020-2023  润新知