命令模式-实现undo和redo

Posted 倾甜xt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了命令模式-实现undo和redo相关的知识,希望对你有一定的参考价值。

 这次实验主要是实现多次redo和undo,即程序的撤回和恢复,这里只实现加法的撤回和恢复。

    程序的撤回和恢复就是由所使用的软件来记录操作步骤,可以将数据恢复到某个操作状态。

    撤回这个指令很常见,Windows系统常用的快捷键ctrl+z就可以实现撤回的效果

    恢复目前只在word等文档编辑软件见到。

      

 

  首先说一下命令模式的结构(参考该类图,使用starUML制作):

    

 

    Adder类是加法的计算和返回计算后的结果, AbstrCommand是抽象类,定义三个抽象方法,AddCommand类继承AbstrCommand类,对AbstrCommand进行扩展,

  这样就可以灵活的改变命令的内容,和添加新命令。CalculatorForm类是调用AbstrCommand的方法,实现操作,并对操作的结果进行处理。通过该结构可以完成撤回和

  回复的实现。

  该结构又优点,这样设计降低了系统的耦合度,也方便加入新命令。

 

  接下来说一下算法实现的原理:

    首先要想撤回和恢复的实现,需要有两个栈(链表也可以),一个栈用来存储操作的每个步骤的结果,命名为撤回栈,另一个表用来

  存储撤回栈弹出的数据,命名为恢复栈。在进行加法操作的时候需要在将最新的结果压入撤回栈(保存最新操作),恢复栈清空(每次进行加法操作,

  需要清空撤回栈弹出的数据),在撤回栈的时候需要将撤回栈的栈顶弹出,并将其压入恢复栈(保存),在恢复时需要将恢复栈的栈顶弹出,并将其

  压入撤回栈,这样就完成了基本的实现,不要忘了再加上栈的空的判断。

 

  栈的使用:stack<Object>  stack = new Stack<Object>();    定义 (说明类型)

       Object j=stack.peek();   返回栈顶元素的值

       Object j=stack.pop();    弹出栈顶元素的值,j是弹出的值

       Object j=stack.push(Object element);    将值压入栈

  源代码:

  //实现加法的计算和返回计算的值

  
1 public class Adder {
2     private int num =0;
3     public int add(int value) {
4         num+=value;
5         return num;
6     }
7 }
Adder类

 

  //抽象命令类

  
1 public abstract class AbstractCommand {
2     public abstract int execute(int value);
3     
4     public abstract int undo();
5     
6     public abstract int redo();
7 }
AbstractCommand 类

 //加法命令类

  
 1 import java.util.Stack;
 2 
 3 
 4 
 5 public class AddCommand extends AbstractCommand {
 6     private Adder adder = new Adder();
 7     private Stack<Integer> unStack = new Stack<Integer>();// 返回栈,用来记录所做的每一步操作,用于撤回
 8     private Stack<Integer> reStack = new Stack<Integer>();// 重复栈,用来存储返回栈弹出的数据,由于重复
 9 
10     /**
11      * 撤回
12      */
13     public int undo() {
14         int i=0;
15         if (unStack.isEmpty()) {
16             
17             i=-1;
18         }else{
19             Integer pop = unStack.pop();
20             reStack.push(pop);
21             if(!unStack.isEmpty()){//判断弹出数据后是否为空,如果为空,说明已撤回到最原始状态
22                 i=unStack.peek();
23             }
24         }
25         return i;
26     }
27 
28     /**
29      * 恢复
30      */
31     public int redo() {
32         int i=0;
33         if (reStack.isEmpty()) {
34             i=-1;
35         }else{//撤回时只要可以可以撤回,则返回栈一定有数据
36             Integer pop = reStack.pop();
37             unStack.push(pop);
38             i=pop;
39         }
40         return i;
41     }
42 
43     /**
44      * 执行计算,并进行栈的更新
45      */
46     public int execute(int value) {
47         int v = 0;
48         if (unStack.isEmpty()) {// 说明还没有数据
49             v = adder.add(value);
50             unStack.push(v);
51         } else {// 需要更新两个栈中的内容,并计算结果,其中返回栈应该更新,重复栈应该清空
52             v = adder.add(value);
53             unStack.push(v);
54             if (!reStack.isEmpty()) {
55                 for (int i = 0; i < reStack.size(); i++) {
56                     reStack.pop();
57                 }
58             }
59         }
60         return v;
61     }
62 }
AddCommand类
  
 1 public class CalculatorForm {
 2     private AbstractCommand command;
 3     public void setCommand(AbstractCommand command) {
 4         this.command =command;
 5     }
 6     /**
 7      * 执行运算
 8      * @param value
 9      */
10     public void compute(int value) {
11         command.execute(value);
12     }
13     /**
14      * 撤回
15      */
16     public void undo() {
17         int i = command.undo();
18         if(i==-1){
19             System.out.println("缓存中已不存在数据");
20         }else{
21             System.out.println("执行成功,运算结果是:"+i);
22         }
23     }
24     /**
25      * 恢复
26      */
27     public void redo() {
28          int i = command.redo();
29         if(i==-1){
30             System.out.println("已恢复至最新数据");
31         }
32         else{
33             System.out.println("执行成功,运算结果是:"+i);
34         }
35     }
36 }
CalculatorForm(引用命令)类

  //测试结果

  
 1 public class client {
 2     public static void main(String[] args) {
 3         CalculatorForm form = new CalculatorForm();
 4         AddCommand command = new AddCommand();
 5         form.setCommand(command);
 6         //计算
 7         System.out.println("------计算过程-----");
 8         form.compute(1);
 9         form.compute(2);
10         form.compute(3);
11         form.compute(4);
12         //多次撤回
13         System.out.println("------撤回过程-----");
14         form.undo();
15         form.undo();
16         form.undo();
17         form.undo();
18         form.undo();
19         //多次恢复
20         System.out.println("------恢复过程-----");
21         form.redo();
22         form.redo();
23         form.redo();
24         form.redo();
25         form.redo();
26     }
27 }
client 类

  实验总结:

    通过本次试验,对命令模式有了基本了解,命令模式很容易实现undo和redo,在本次试验我使用了stack栈用来实现多次的撤回和恢复,透彻理解使用两个栈,用来对数据

进行恢复的原理,就可以很快的解决该问题。

 

以上是关于命令模式-实现undo和redo的主要内容,如果未能解决你的问题,请参考以下文章

CAD中的“返回”和“前进”命令的快捷方式分别是“undo”和“redo”

undo/redo功能的原理和思路

命令模式

编辑器的模式(1)—undo/redo

online redo log 和undo各有啥作用

在数据库中,REDO操作和UNDO操纵个表示啥含义?