文本编辑器中,如何设计 撤销/重复栈
          文本编辑器中,如何设计 撤销/重复栈
电子科技大学软件学院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>
 /// 撤销重复操作管理器
    /// 撤销重复操作管理器
 /// </summary>
    /// </summary>
 class UndoStackManager
    class UndoStackManager
 {
    {
 /// <summary>
        /// <summary>
 /// 撤销栈
        /// 撤销栈
 /// </summary>
        /// </summary>
 Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
        Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
 /// <summary>
        /// <summary>
 /// 重复栈
        /// 重复栈
 /// </summary>
        /// </summary>
 Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
        Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();


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

 /// <summary>
        /// <summary>
 /// 获取一个值,指示是否有可撤销的操作
        /// 获取一个值,指示是否有可撤销的操作
 /// </summary>
        /// </summary>
 public bool CanUndo
        public bool CanUndo
 {
        {
 get
            get
 {
            {
 return un_stack.Count != 0;
                return un_stack.Count != 0;
 }
            }
 }
        }

 /// <summary>
        /// <summary>
 /// 获取一个值,指示是否有可重复的操作
        /// 获取一个值,指示是否有可重复的操作
 /// </summary>
        /// </summary>
 public bool CanRedo
        public bool CanRedo
 {
        {
 get
            get
 {
            {
 return re_stack.Count != 0;
                return re_stack.Count != 0;
 }
            }
 }
        }

 /// <summary>
        /// <summary>
 /// 撤销上一操作
        /// 撤销上一操作
 /// </summary>
        /// </summary>
 public void Undo()
        public void Undo()
 {
        {
 if (this.CanUndo)
            if (this.CanUndo)
 {
            {
 IUndoableOperate op = un_stack.Pop();
                IUndoableOperate op = un_stack.Pop();
 op.Undo();
                op.Undo();
 re_stack.Push(op);
                re_stack.Push(op);
 }
            }
 }
        }

 /// <summary>
        /// <summary>
 /// 重复被撤销的操作
        /// 重复被撤销的操作
 /// </summary>
        /// </summary>
 public void Redo()
        public void Redo()
 {
        {
 if (this.CanRedo)
            if (this.CanRedo)
 {
            {
 IUndoableOperate op = re_stack.Pop();
                IUndoableOperate op = re_stack.Pop();
 op.Redo();
                op.Redo();
 un_stack.Push(op);
                un_stack.Push(op);
 }
            }
 }
        }


 /// <summary>
        /// <summary>
 /// 将某一操作存放到撤销栈中
        /// 将某一操作存放到撤销栈中
 /// </summary>
        /// </summary>
 /// <param name="op"></param>
        /// <param name="op"></param>
 public void PushToUndoStack(IUndoableOperate op)
        public void PushToUndoStack(IUndoableOperate op)
 {
        {
 this.un_stack.Push(op);
            this.un_stack.Push(op);
 this.re_stack.Clear();
            this.re_stack.Clear();
 }
        }
 }
    }
以下是完整的示例代码:
 完整的示例代码
完整的示例代码
示例代码的执行结果是:
do operate1
do operate2
undo operate2
undo operate1
do operate1
do operate2
电子科技大学软件学院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> /// 撤销重复操作管理器
    /// 撤销重复操作管理器 /// </summary>
    /// </summary> class UndoStackManager
    class UndoStackManager {
    { /// <summary>
        /// <summary> /// 撤销栈
        /// 撤销栈 /// </summary>
        /// </summary> Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
        Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>(); /// <summary>
        /// <summary> /// 重复栈
        /// 重复栈 /// </summary>
        /// </summary> Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
        Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();

 public void ClearStack()
        public void ClearStack() {
        { this.un_stack.Clear();
            this.un_stack.Clear(); this.re_stack.Clear();
            this.re_stack.Clear(); }
        }
 /// <summary>
        /// <summary> /// 获取一个值,指示是否有可撤销的操作
        /// 获取一个值,指示是否有可撤销的操作 /// </summary>
        /// </summary> public bool CanUndo
        public bool CanUndo {
        { get
            get {
            { return un_stack.Count != 0;
                return un_stack.Count != 0; }
            } }
        }
 /// <summary>
        /// <summary> /// 获取一个值,指示是否有可重复的操作
        /// 获取一个值,指示是否有可重复的操作 /// </summary>
        /// </summary> public bool CanRedo
        public bool CanRedo {
        { get
            get {
            { return re_stack.Count != 0;
                return re_stack.Count != 0; }
            } }
        }
 /// <summary>
        /// <summary> /// 撤销上一操作
        /// 撤销上一操作 /// </summary>
        /// </summary> public void Undo()
        public void Undo() {
        { if (this.CanUndo)
            if (this.CanUndo) {
            { IUndoableOperate op = un_stack.Pop();
                IUndoableOperate op = un_stack.Pop(); op.Undo();
                op.Undo(); re_stack.Push(op);
                re_stack.Push(op); }
            } }
        }
 /// <summary>
        /// <summary> /// 重复被撤销的操作
        /// 重复被撤销的操作 /// </summary>
        /// </summary> public void Redo()
        public void Redo() {
        { if (this.CanRedo)
            if (this.CanRedo) {
            { IUndoableOperate op = re_stack.Pop();
                IUndoableOperate op = re_stack.Pop(); op.Redo();
                op.Redo(); un_stack.Push(op);
                un_stack.Push(op); }
            } }
        }

 /// <summary>
        /// <summary> /// 将某一操作存放到撤销栈中
        /// 将某一操作存放到撤销栈中 /// </summary>
        /// </summary> /// <param name="op"></param>
        /// <param name="op"></param> public void PushToUndoStack(IUndoableOperate op)
        public void PushToUndoStack(IUndoableOperate op) {
        { this.un_stack.Push(op);
            this.un_stack.Push(op); this.re_stack.Clear();
            this.re_stack.Clear(); }
        } }
    }以下是完整的示例代码:
 完整的示例代码
完整的示例代码示例代码的执行结果是:
do operate1
do operate2
undo operate2
undo operate1
do operate1
do operate2
 
                     
                    
                 
                    
                
 
         
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号