是什么
命令模式允许将请求封装成一个对象(命令对象,包含执行操作所需的所有信息),并将命令对象按照一定的顺序存储在队列中,然后再逐一调用执行,这些命令也可以支持反向操作,进行撤销和重做。
这样发送者只需要触发命令就可以完成操作,不需要知道接受者的具体操作,从而实现两者间的解耦。
基本结构
- 命令接口
Command
:接口或者抽象类,定义执行操作的接口。
- 具体命令类
ConcreteCommand
: 实现命令接口,执行具体操作,在调用execute
方法时使“接收者对象”根据命令完成具体的任务,比如遥控器中的“开机”,“关机”命令。
- 接收者类
Receiver
: 接受并执行命令的对象,可以是任何对象,遥控器可以控制空调,也可以控制电视机,电视机和空调负责执行具体操作,是接收者。
- 调用者类
Invoker
: 发起请求的对象,有一个将命令作为参数传递的方法。它不关心命令的具体实现,只负责调用命令对象的 execute()
方法来传递请求,在本例中,控制遥控器的“人”就是调用者。
简易实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public interface Command { void execute(); }
public class ConcreteCommand implements Command { private Receiver receiver;
public ConcreteCommand(Receiver receiver) { this.receiver = receiver; }
@Override public void execute() { receiver.action(); } }
public class Receiver { public void action() { } }
public class Invoker { private Command command;
public Invoker(Command command) { this.command = command; }
public void executeCommand() { command.execute(); } }
class Invoker { private Queue<Command> commandQueue; private Stack<Command> undoStack;
public Invoker() { this.commandQueue = new LinkedList<>(); this.undoStack = new Stack<>(); }
public void setAndExecuteCommand(Command command) { command.execute(); commandQueue.offer(command); undoStack.push(command); }
public void undoLastCommand() { if (!undoStack.isEmpty()) { Command lastCommand = undoStack.pop(); lastCommand.undo(); commandQueue.remove(lastCommand); } else { System.out.println("No command to undo."); } }
public void executeCommandsInQueue() { for (Command command : commandQueue) { command.execute(); } } }
public class Main { public static void main(String[] args) { Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoker invoker = new Invoker(command);
invoker.executeCommand(); } }
|
优缺点
命令模式在需要将请求封装成对象、支持撤销和重做、设计命令队列等情况下,都是一个有效的设计模式。
- 撤销操作: 需要支持撤销操作,命令模式可以存储历史命令,轻松实现撤销功能。
- 队列请求: 命令模式可以将请求排队,形成一个命令队列,依次执行命令。
- 可扩展性: 可以很容易地添加新的命令类和接收者类,而不影响现有的代码。新增命令不需要修改现有代码,符合开闭原则。
但是对于每个命令,都会有一个具体命令类,这可能导致类的数量急剧增加,增加了系统的复杂性。
命令模式同样有着很多现实场景的应用,比如Git中的很多操作,如提交(commit)、合并(merge)等,都可以看作是命令模式的应用,用户通过执行相应的命令来操作版本库。Java的GUI编程中,很多事件处理机制也都使用了命令模式。例如,每个按钮都有一个关联的 Action
,它代表一个命令,按钮的点击触发 Action
的执行。