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

本文介绍了一种在文本编辑器中实现撤销和重复功能的方法。通过定义一个接口,所有可撤销的操作都遵循该接口,利用栈来保存操作,实现了统一管理和撤销功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

                                                  电子科技大学软件学院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()函数时,其能被正确的执行吗?答案是肯定的,这利用了多态。

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

None.gif      class  UndoStackManager
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 撤销栈
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 重复栈
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
InBlock.gif
InBlock.gif
InBlock.gif        
public void ClearStack()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
this.un_stack.Clear();
InBlock.gif            
this.re_stack.Clear();
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 获取一个值,指示是否有可撤销的操作
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public bool CanUndo
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return un_stack.Count != 0;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 获取一个值,指示是否有可重复的操作
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public bool CanRedo
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return re_stack.Count != 0;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 撤销上一操作
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public void Undo()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (this.CanUndo)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                IUndoableOperate op 
= un_stack.Pop();
InBlock.gif                op.Undo();
InBlock.gif                re_stack.Push(op);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 重复被撤销的操作
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public void Redo()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (this.CanRedo)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                IUndoableOperate op 
= re_stack.Pop();
InBlock.gif                op.Redo();
InBlock.gif                un_stack.Push(op);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 将某一操作存放到撤销栈中
InBlock.gif        
/// </summary>
ExpandedSubBlockEnd.gif        
/// <param name="op"></param>

InBlock.gif        public void PushToUndoStack(IUndoableOperate op)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
this.un_stack.Push(op);
InBlock.gif            
this.re_stack.Clear();
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

     以下是完整的示例代码:
ContractedBlock.gif ExpandedBlockStart.gif 完整的示例代码
  1None.gifusing System;
  2None.gifusing System.Collections.Generic;
  3None.gifusing System.Text;
  4None.gif
  5None.gifnamespace UndoRedo
  6ExpandedBlockStart.gifContractedBlock.gifdot.gif{
  7InBlock.gif    class Program
  8ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
  9InBlock.gif        static void Main(string[] args)
 10ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 11InBlock.gif            //声明一个管理器
 12InBlock.gif            UndoStackManager manager = new UndoStackManager();
 13InBlock.gif
 14InBlock.gif            //执行一些操作
 15InBlock.gif            Operate1 op1 = new Operate1();
 16InBlock.gif            op1.Execute();
 17InBlock.gif            manager.PushToUndoStack(op1);
 18InBlock.gif
 19InBlock.gif            Operate2 op2 = new Operate2();
 20InBlock.gif            op2.Execute();
 21InBlock.gif            manager.PushToUndoStack(op2);
 22InBlock.gif
 23InBlock.gif            //撤销执行的操作
 24InBlock.gif            manager.Undo();
 25InBlock.gif            manager.Undo();
 26InBlock.gif
 27InBlock.gif            //重复被撤销的操作
 28InBlock.gif            manager.Redo();
 29InBlock.gif            manager.Redo();
 30ExpandedSubBlockEnd.gif        }

 31ExpandedSubBlockEnd.gif    }

 32InBlock.gif
 33InBlock.gif
 34ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 35InBlock.gif    /// 可撤销重复操作接口。
 36InBlock.gif    /// 所有的可重复可撤销的操作都应该继承这个接口。
 37ExpandedSubBlockEnd.gif    /// </summary>

 38InBlock.gif    interface IUndoableOperate
 39ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 40InBlock.gif        void Undo();
 41InBlock.gif        void Redo();
 42InBlock.gif        void Execute();
 43ExpandedSubBlockEnd.gif    }

 44InBlock.gif
 45ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 46InBlock.gif    /// 操作1
 47ExpandedSubBlockEnd.gif    /// </summary>

 48InBlock.gif    class Operate1 : IUndoableOperate
 49ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 50ContractedSubBlock.gifExpandedSubBlockStart.gif        IUndoableOperate 成员#region IUndoableOperate 成员
 51InBlock.gif
 52ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 53InBlock.gif        /// 撤销该操作时执行
 54ExpandedSubBlockEnd.gif        /// </summary>

 55InBlock.gif        public void Undo()
 56ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 57InBlock.gif            Console.WriteLine("undo operate1");
 58ExpandedSubBlockEnd.gif        }

 59InBlock.gif
 60ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 61InBlock.gif        /// 重复该操作时执行
 62ExpandedSubBlockEnd.gif        /// </summary>

 63InBlock.gif        public void Redo()
 64ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 65InBlock.gif            Console.WriteLine("do operate1");
 66ExpandedSubBlockEnd.gif        }

 67InBlock.gif
 68ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 69InBlock.gif        /// 执行操作1
 70ExpandedSubBlockEnd.gif        /// </summary>

 71InBlock.gif        public void Execute()
 72ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 73InBlock.gif            this.Redo();
 74ExpandedSubBlockEnd.gif        }

 75InBlock.gif
 76ExpandedSubBlockEnd.gif        #endregion

 77InBlock.gif
 78ExpandedSubBlockEnd.gif    }

 79InBlock.gif
 80InBlock.gif
 81ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 82InBlock.gif    /// 操作1
 83ExpandedSubBlockEnd.gif    /// </summary>

 84InBlock.gif    class Operate2 : IUndoableOperate
 85ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 86ContractedSubBlock.gifExpandedSubBlockStart.gif        IUndoableOperate 成员#region IUndoableOperate 成员
 87InBlock.gif
 88ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 89InBlock.gif        /// 撤销该操作时执行
 90ExpandedSubBlockEnd.gif        /// </summary>

 91InBlock.gif        public void Undo()
 92ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 93InBlock.gif            Console.WriteLine("undo operate2");
 94ExpandedSubBlockEnd.gif        }

 95InBlock.gif
 96ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 97InBlock.gif        /// 重复该操作时执行
 98ExpandedSubBlockEnd.gif        /// </summary>

 99InBlock.gif        public void Redo()
100ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
101InBlock.gif            Console.WriteLine("do operate2");
102ExpandedSubBlockEnd.gif        }

103InBlock.gif
104ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
105InBlock.gif        /// 执行操作1
106ExpandedSubBlockEnd.gif        /// </summary>

107InBlock.gif        public void Execute()
108ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
109InBlock.gif            this.Redo();
110ExpandedSubBlockEnd.gif        }

111InBlock.gif
112ExpandedSubBlockEnd.gif        #endregion

113ExpandedSubBlockEnd.gif    }

114InBlock.gif
115ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
116InBlock.gif    /// 撤销重复操作管理器
117ExpandedSubBlockEnd.gif    /// </summary>

118InBlock.gif    class UndoStackManager
119ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
120ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
121InBlock.gif        /// 撤销栈
122ExpandedSubBlockEnd.gif        /// </summary>

123InBlock.gif        Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
124ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
125InBlock.gif        /// 重复栈
126ExpandedSubBlockEnd.gif        /// </summary>

127InBlock.gif        Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
128InBlock.gif
129InBlock.gif
130InBlock.gif        public void ClearStack()
131ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
132InBlock.gif            this.un_stack.Clear();
133InBlock.gif            this.re_stack.Clear();
134ExpandedSubBlockEnd.gif        }

135InBlock.gif
136ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
137InBlock.gif        /// 获取一个值,指示是否有可撤销的操作
138ExpandedSubBlockEnd.gif        /// </summary>

139InBlock.gif        public bool CanUndo
140ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
141InBlock.gif            get
142ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
143InBlock.gif                return un_stack.Count != 0;
144ExpandedSubBlockEnd.gif            }

145ExpandedSubBlockEnd.gif        }

146InBlock.gif
147ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
148InBlock.gif        /// 获取一个值,指示是否有可重复的操作
149ExpandedSubBlockEnd.gif        /// </summary>

150InBlock.gif        public bool CanRedo
151ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
152InBlock.gif            get
153ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
154InBlock.gif                return re_stack.Count != 0;
155ExpandedSubBlockEnd.gif            }

156ExpandedSubBlockEnd.gif        }

157InBlock.gif
158ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
159InBlock.gif        /// 撤销上一操作
160ExpandedSubBlockEnd.gif        /// </summary>

161InBlock.gif        public void Undo()
162ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
163InBlock.gif            if (this.CanUndo)
164ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
165InBlock.gif                IUndoableOperate op = un_stack.Pop();
166InBlock.gif                op.Undo();
167InBlock.gif                re_stack.Push(op);
168ExpandedSubBlockEnd.gif            }

169ExpandedSubBlockEnd.gif        }

170InBlock.gif
171ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
172InBlock.gif        /// 重复被撤销的操作
173ExpandedSubBlockEnd.gif        /// </summary>

174InBlock.gif        public void Redo()
175ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
176InBlock.gif            if (this.CanRedo)
177ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
178InBlock.gif                IUndoableOperate op = re_stack.Pop();
179InBlock.gif                op.Redo();
180InBlock.gif                un_stack.Push(op);
181ExpandedSubBlockEnd.gif            }

182ExpandedSubBlockEnd.gif        }

183InBlock.gif
184InBlock.gif
185ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
186InBlock.gif        /// 将某一操作存放到撤销栈中
187InBlock.gif        /// </summary>
188ExpandedSubBlockEnd.gif        /// <param name="op"></param>

189InBlock.gif        public void PushToUndoStack(IUndoableOperate op)
190ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
191InBlock.gif            this.un_stack.Push(op);
192InBlock.gif            this.re_stack.Clear();
193ExpandedSubBlockEnd.gif        }

194ExpandedSubBlockEnd.gif    }

195ExpandedBlockEnd.gif}

196None.gif

示例代码的执行结果是:
do operate1
do operate2
undo operate2
undo operate1
do operate1
do operate2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值