文本编辑器中,如何设计 撤销/重复栈
电子科技大学软件学院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
电子科技大学软件学院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();
}
}
以下是完整的示例代码:
完整的示例代码
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace UndoRedo
6{
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //声明一个管理器
12 UndoStackManager manager = new UndoStackManager();
13
14 //执行一些操作
15 Operate1 op1 = new Operate1();
16 op1.Execute();
17 manager.PushToUndoStack(op1);
18
19 Operate2 op2 = new Operate2();
20 op2.Execute();
21 manager.PushToUndoStack(op2);
22
23 //撤销执行的操作
24 manager.Undo();
25 manager.Undo();
26
27 //重复被撤销的操作
28 manager.Redo();
29 manager.Redo();
30 }
31 }
32
33
34 /**//// <summary>
35 /// 可撤销重复操作接口。
36 /// 所有的可重复可撤销的操作都应该继承这个接口。
37 /// </summary>
38 interface IUndoableOperate
39 {
40 void Undo();
41 void Redo();
42 void Execute();
43 }
44
45 /**//// <summary>
46 /// 操作1
47 /// </summary>
48 class Operate1 : IUndoableOperate
49 {
50 IUndoableOperate 成员#region IUndoableOperate 成员
51
52 /**//// <summary>
53 /// 撤销该操作时执行
54 /// </summary>
55 public void Undo()
56 {
57 Console.WriteLine("undo operate1");
58 }
59
60 /**//// <summary>
61 /// 重复该操作时执行
62 /// </summary>
63 public void Redo()
64 {
65 Console.WriteLine("do operate1");
66 }
67
68 /**//// <summary>
69 /// 执行操作1
70 /// </summary>
71 public void Execute()
72 {
73 this.Redo();
74 }
75
76 #endregion
77
78 }
79
80
81 /**//// <summary>
82 /// 操作1
83 /// </summary>
84 class Operate2 : IUndoableOperate
85 {
86 IUndoableOperate 成员#region IUndoableOperate 成员
87
88 /**//// <summary>
89 /// 撤销该操作时执行
90 /// </summary>
91 public void Undo()
92 {
93 Console.WriteLine("undo operate2");
94 }
95
96 /**//// <summary>
97 /// 重复该操作时执行
98 /// </summary>
99 public void Redo()
100 {
101 Console.WriteLine("do operate2");
102 }
103
104 /**//// <summary>
105 /// 执行操作1
106 /// </summary>
107 public void Execute()
108 {
109 this.Redo();
110 }
111
112 #endregion
113 }
114
115 /**//// <summary>
116 /// 撤销重复操作管理器
117 /// </summary>
118 class UndoStackManager
119 {
120 /**//// <summary>
121 /// 撤销栈
122 /// </summary>
123 Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
124 /**//// <summary>
125 /// 重复栈
126 /// </summary>
127 Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
128
129
130 public void ClearStack()
131 {
132 this.un_stack.Clear();
133 this.re_stack.Clear();
134 }
135
136 /**//// <summary>
137 /// 获取一个值,指示是否有可撤销的操作
138 /// </summary>
139 public bool CanUndo
140 {
141 get
142 {
143 return un_stack.Count != 0;
144 }
145 }
146
147 /**//// <summary>
148 /// 获取一个值,指示是否有可重复的操作
149 /// </summary>
150 public bool CanRedo
151 {
152 get
153 {
154 return re_stack.Count != 0;
155 }
156 }
157
158 /**//// <summary>
159 /// 撤销上一操作
160 /// </summary>
161 public void Undo()
162 {
163 if (this.CanUndo)
164 {
165 IUndoableOperate op = un_stack.Pop();
166 op.Undo();
167 re_stack.Push(op);
168 }
169 }
170
171 /**//// <summary>
172 /// 重复被撤销的操作
173 /// </summary>
174 public void Redo()
175 {
176 if (this.CanRedo)
177 {
178 IUndoableOperate op = re_stack.Pop();
179 op.Redo();
180 un_stack.Push(op);
181 }
182 }
183
184
185 /**//// <summary>
186 /// 将某一操作存放到撤销栈中
187 /// </summary>
188 /// <param name="op"></param>
189 public void PushToUndoStack(IUndoableOperate op)
190 {
191 this.un_stack.Push(op);
192 this.re_stack.Clear();
193 }
194 }
195}
196
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace UndoRedo
6{
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //声明一个管理器
12 UndoStackManager manager = new UndoStackManager();
13
14 //执行一些操作
15 Operate1 op1 = new Operate1();
16 op1.Execute();
17 manager.PushToUndoStack(op1);
18
19 Operate2 op2 = new Operate2();
20 op2.Execute();
21 manager.PushToUndoStack(op2);
22
23 //撤销执行的操作
24 manager.Undo();
25 manager.Undo();
26
27 //重复被撤销的操作
28 manager.Redo();
29 manager.Redo();
30 }
31 }
32
33
34 /**//// <summary>
35 /// 可撤销重复操作接口。
36 /// 所有的可重复可撤销的操作都应该继承这个接口。
37 /// </summary>
38 interface IUndoableOperate
39 {
40 void Undo();
41 void Redo();
42 void Execute();
43 }
44
45 /**//// <summary>
46 /// 操作1
47 /// </summary>
48 class Operate1 : IUndoableOperate
49 {
50 IUndoableOperate 成员#region IUndoableOperate 成员
51
52 /**//// <summary>
53 /// 撤销该操作时执行
54 /// </summary>
55 public void Undo()
56 {
57 Console.WriteLine("undo operate1");
58 }
59
60 /**//// <summary>
61 /// 重复该操作时执行
62 /// </summary>
63 public void Redo()
64 {
65 Console.WriteLine("do operate1");
66 }
67
68 /**//// <summary>
69 /// 执行操作1
70 /// </summary>
71 public void Execute()
72 {
73 this.Redo();
74 }
75
76 #endregion
77
78 }
79
80
81 /**//// <summary>
82 /// 操作1
83 /// </summary>
84 class Operate2 : IUndoableOperate
85 {
86 IUndoableOperate 成员#region IUndoableOperate 成员
87
88 /**//// <summary>
89 /// 撤销该操作时执行
90 /// </summary>
91 public void Undo()
92 {
93 Console.WriteLine("undo operate2");
94 }
95
96 /**//// <summary>
97 /// 重复该操作时执行
98 /// </summary>
99 public void Redo()
100 {
101 Console.WriteLine("do operate2");
102 }
103
104 /**//// <summary>
105 /// 执行操作1
106 /// </summary>
107 public void Execute()
108 {
109 this.Redo();
110 }
111
112 #endregion
113 }
114
115 /**//// <summary>
116 /// 撤销重复操作管理器
117 /// </summary>
118 class UndoStackManager
119 {
120 /**//// <summary>
121 /// 撤销栈
122 /// </summary>
123 Stack<IUndoableOperate> un_stack = new Stack<IUndoableOperate>();
124 /**//// <summary>
125 /// 重复栈
126 /// </summary>
127 Stack<IUndoableOperate> re_stack = new Stack<IUndoableOperate>();
128
129
130 public void ClearStack()
131 {
132 this.un_stack.Clear();
133 this.re_stack.Clear();
134 }
135
136 /**//// <summary>
137 /// 获取一个值,指示是否有可撤销的操作
138 /// </summary>
139 public bool CanUndo
140 {
141 get
142 {
143 return un_stack.Count != 0;
144 }
145 }
146
147 /**//// <summary>
148 /// 获取一个值,指示是否有可重复的操作
149 /// </summary>
150 public bool CanRedo
151 {
152 get
153 {
154 return re_stack.Count != 0;
155 }
156 }
157
158 /**//// <summary>
159 /// 撤销上一操作
160 /// </summary>
161 public void Undo()
162 {
163 if (this.CanUndo)
164 {
165 IUndoableOperate op = un_stack.Pop();
166 op.Undo();
167 re_stack.Push(op);
168 }
169 }
170
171 /**//// <summary>
172 /// 重复被撤销的操作
173 /// </summary>
174 public void Redo()
175 {
176 if (this.CanRedo)
177 {
178 IUndoableOperate op = re_stack.Pop();
179 op.Redo();
180 un_stack.Push(op);
181 }
182 }
183
184
185 /**//// <summary>
186 /// 将某一操作存放到撤销栈中
187 /// </summary>
188 /// <param name="op"></param>
189 public void PushToUndoStack(IUndoableOperate op)
190 {
191 this.un_stack.Push(op);
192 this.re_stack.Clear();
193 }
194 }
195}
196
示例代码的执行结果是:
do operate1
do operate2
undo operate2
undo operate1
do operate1
do operate2