1、定义

将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

2、模式结构

命令(Command)模式 - 图1

命令模式由四部分组成:

  • Command(抽象命令角色):定义命令的接口,声明执行的方法。
  • ConcreteCommand(具体命令角色):命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • Receiver(接受者):真正执行命令的对象。
  • Invoker(调用者):要求命令对象执行请求,通常会持有命令对象,也可以持有很多的命令对象。这个是客户端真的触发命令并要求命令执行相应的地方,也就是是相当于使用命令对象的入口。

3、实例

3.1 Command

  1. public interface Command {
  2. public void execute();
  3. // 撤销操作
  4. public void undo();
  5. }

3.2 开灯/关灯/空命令(ConcreteCommand)

  1. public class LightOnCommand implements Command {
  2. private LightReceiver light;
  3. public LightOnCommand(LightReceiver light) {
  4. this.light = light;
  5. }
  6. @Override
  7. public void execute() {
  8. // 调用接收者方法
  9. light.on();
  10. }
  11. @Override
  12. public void undo() {
  13. light.off();
  14. }
  15. }
  1. public class LightOffCommand implements Command {
  2. private LightReceiver light;
  3. public LightOffCommand(LightReceiver light) {
  4. this.light = light;
  5. }
  6. @Override
  7. public void execute() {
  8. // 调用接收者方法
  9. light.off();
  10. }
  11. @Override
  12. public void undo() {
  13. light.on();
  14. }
  15. }
  1. public class NoCommand implements Command {
  2. @Override
  3. public void execute() {
  4. }
  5. @Override
  6. public void undo() {
  7. }
  8. }

3.3 LightReceiver(Receiver)

  1. public class LightReceiver {
  2. public void on() {
  3. System.out.println("电灯打开了。。。");
  4. }
  5. public void off() {
  6. System.out.println("电灯关闭了。。。");
  7. }
  8. }

3.4 遥控器(Invoker)

  1. public class RemoteController {
  2. // 开关按钮的命令数组
  3. private Command[] onCommands;
  4. private Command[] offCommands;
  5. private Command undoCommand;
  6. // 完成按钮的初始化
  7. public RemoteController() {
  8. onCommands = new Command[5];
  9. offCommands = new Command[5];
  10. for(int i = 0; i < 5; i++) {
  11. onCommands[i] = new NoCommand();
  12. offCommands[i] = new NoCommand();
  13. }
  14. }
  15. // 设置你需要的命令
  16. public void setCommand(int no, Command onCommand, Command offCommand) {
  17. onCommands[no] = onCommand;
  18. offCommands[no] = offCommand;
  19. }
  20. // 按开按钮
  21. public void onButtonWasPushed(int no) {
  22. onCommands[no].execute();
  23. // 记住这次操作,用于撤销
  24. undoCommand = onCommands[no];
  25. }
  26. // 按关按钮
  27. public void offButtonWasPushed(int no) {
  28. offCommands[no].execute();
  29. // 记住这次操作,用于撤销
  30. undoCommand = offCommands[no];
  31. }
  32. // 撤销按钮
  33. public void undoButtonWasPushed() {
  34. undoCommand.undo();
  35. }
  36. }

3.5 客户端调用

  1. public class Client {
  2. public static void main(String[] args) {
  3. LightReceiver light = new LightReceiver();
  4. LightOnCommand lightOnCommand = new LightOnCommand(light);
  5. LightOffCommand lightOffCommand = new LightOffCommand(light);
  6. // 调用者
  7. RemoteController remoteController = new RemoteController();
  8. remoteController.setCommand(0, lightOnCommand, lightOffCommand);
  9. System.out.println("------按下开灯按钮------");
  10. remoteController.onButtonWasPushed(0);
  11. System.out.println("------按下关灯按钮------");
  12. remoteController.offButtonWasPushed(0);
  13. System.out.println("------按下撤销按钮------");
  14. remoteController.undoButtonWasPushed();
  15. }
  16. }

4、适用场景

  • 需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • 需要在不同的时间制定请求,将请求排队和执行请求。
  • 需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  • 徐亚奥将操作组合在一起,即支持宏命令。

5、在Spring中的应用

命令(Command)模式 - 图2

分析:

  • StatementCallback类似于命令接口(Command)。
  • QueryStatementCallback,匿名内部类,实现了命令接口,同时充当命令的接收者。
  • JdbcTemplate,命令调用者,其中execute方法中,调用了action.doInStatement方法。

6、优缺点

6.1 优点
  • 降低系统的耦合度。
  • 新的命令可以很容易地加入到系统中。
  • 可以比较容易地设计一个命令队列和宏命令(组合命令)。
  • 可以方便地实现对请求的Undo和Redo。

6.2 缺点
  • 可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因为某些系统可能需要大量具体命令类,这将影响命令模式的使用。