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

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

                                                  电子科技大学软件学院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
posted @ 2006-10-04 00:48  周银辉  阅读(2327)  评论(0编辑  收藏  举报