请求发送者与接收者解耦——命令模式(四)

5 撤销操作的实现

在命令模式中,我们可以通过调用一个命令对象的execute()方法来实现对请求的处理,如果需要撤销(Undo)请求,可通过在命令类中增加一个逆向操作来实现。

扩展

除了通过一个逆向操作来实现撤销(Undo)外,还可以通过保存对象的历史状态来实现撤销,后者可使用备忘录模式(Memento Pattern)来实现。

下面通过一个简单的实例来学习如何使用命令模式实现撤销操作:

Sunny软件公司欲开发一个简易计算器,该计算器可以实现简单的数学运算,还可以对运算实施撤销操作。

Sunny软件公司开发人员使用命令模式设计了如图5所示结构图,其中计算器界面类CalculatorForm充当请求发送者,实现了数据求和功能的加法类Adder充当请求接收者,界面类可间接调用加法类中的add()方法实现加法运算,并且提供了可撤销加法运算的undo()方法。

请求发送者与接收者解耦——命令模式(四) - 图1

图5 简易计算器结构图

本实例完整代码如下所示:

  1. //加法类:请求接收者
  2. class Adder {
  3. private int num=0; //定义初始值为0
  4. //加法操作,每次将传入的值与num作加法运算,再将结果返回
  5. public int add(int value) {
  6. num += value;
  7. return num;
  8. }
  9. }
  10. //抽象命令类
  11. abstract class AbstractCommand {
  12. public abstract int execute(int value); //声明命令执行方法execute()
  13. public abstract int undo(); //声明撤销方法undo()
  14. }
  15. //具体命令类
  16. class ConcreteCommand extends AbstractCommand {
  17. private Adder adder = new Adder();
  18. private int value;
  19. //实现抽象命令类中声明的execute()方法,调用加法类的加法操作
  20. public int execute(int value) {
  21. this.value=value;
  22. return adder.add(value);
  23. }
  24. //实现抽象命令类中声明的undo()方法,通过加一个相反数来实现加法的逆向操作
  25. public int undo() {
  26. return adder.add(-value);
  27. }
  28. }
  29. //计算器界面类:请求发送者
  30. class CalculatorForm {
  31. private AbstractCommand command;
  32. public void setCommand(AbstractCommand command) {
  33. this.command = command;
  34. }
  35. //调用命令对象的execute()方法执行运算
  36. public void compute(int value) {
  37. int i = command.execute(value);
  38. System.out.println("执行运算,运算结果为:" + i);
  39. }
  40. //调用命令对象的undo()方法执行撤销
  41. public void undo() {
  42. int i = command.undo();
  43. System.out.println("执行撤销,运算结果为:" + i);
  44. }
  45. }

编写如下客户端测试代码:

  1. class Client {
  2. public static void main(String args[]) {
  3. CalculatorForm form = new CalculatorForm();
  4. AbstractCommand command;
  5. command = new ConcreteCommand();
  6. form.setCommand(command); //向发送者注入命令对象
  7. form.compute(10);
  8. form.compute(5);
  9. form.compute(10);
  10. form.undo();
  11. }
  12. }

编译并运行程序,输出结果如下:

  1. 执行运算,运算结果为:10
  2. 执行运算,运算结果为:15
  3. 执行运算,运算结果为:25
  4. 执行撤销,运算结果为:15

思考

如果连续调用“form.undo()”两次,预测客户端代码的输出结果。

需要注意的是在本实例中只能实现一步撤销操作,因为没有保存命令对象的历史状态,可以通过引入一个命令集合或其他方式来存储每一次操作时命令的状态,从而实现多次撤销操作。除了Undo操作外,还可以采用类似的方式实现恢复(Redo)操作,即恢复所撤销的操作(或称为二次撤销)。

练习

修改简易计算器源代码,使之能够实现多次撤销(Undo)和恢复(Redo)。