每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方接收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,==使得请求的一方不必知道接收请求的一方的接口==,更不必知道请求是怎么被接收、以及操作是否被执行、何时被执行、怎么被执行的。
主要是调用者与接收者隔离,调用者调用具体的命令,命令去调用具体的接收者执行。
- Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作
- ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。[有一个Receiver作为成员变量]
- Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
- Receiver(接收者):接收者请求与执行相关的操作,它具体实现对请求的业务处理。
抽象命令角色类
1 | public interface Command { |
具体命令角色类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class ConcreteCommand implements Command {
/**
* 持有相应的接收者对象
*/
private Receiver receiver = null;
/**
* 构造方法
* @param receiver
*/
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
//通常会转调接收者的形影方法,让接收者来真正执行功能
receiver.action();
}
}
接收者角色类
1 | public class Receiver { |
请求者角色类
1 | public class Invoker { |
调用
1 | public class Client { |
实例
下面以电视机的开、关命令为例,进行讲解:
角色:
- 接收者—电视
- 命令— 开、关
- 调用者— 遥控器
- 客户端—人
1 | /** |
1 | /** |
1 | /** |
1 | /** |
1 | /** |
1 | /** |
优缺点
优点:
- 命令模式将行为调用者和接收者,降低程序的耦合,便于程序扩展;
- 命令模式将行为的具体实现封装起来,客户端无需关心行为的具体实现;
- 命令模式可为多种行为提供统一的调用入口,便于程序对行为的管理和控制;(组合命令)
缺点:
- 使用命令模式的话,不用管命令多简单,都需要写一个命令类来封装,使用命令模式可能会导致系统有过多的具体命令类;
扩展
命令队列
主要差别在于invoker中存在一个队列,客户端将命令交给invoker后,invoker将命令保存在队列中,客户端调用call的时候统一执行(针对队列中的命令什么时候执行可以自定义。)
核心代码
带有队列的Invoker1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Invoker {
private CommandQueue commandQueue; //维持一个CommandQueue对象的引用
//构造注入
public Invoker(CommandQueue commandQueue) {
this. commandQueue = commandQueue;
}
//设值注入
public void setCommandQueue(CommandQueue commandQueue) {
this.commandQueue = commandQueue;
}
//调用CommandQueue类的execute()方法
public void call() {
commandQueue.execute();
}
}
队列的详细实现
1 | public class CommandQueue { |
实例讲解
还是电视的例子,这次的遥控器比较奇怪,是一个batch模式的遥控器
1 | public class Client { |
1 | /** |
1 |
|
宏命令
宏命令是讲多个命令组合成一个命令,感觉就是组合+重命名吧。具体实现是新增一个command的实现,这个实现里头是包含一个对列,存放对应的多个细粒度执行。
1 | /** |
1 | /** |
实例讲解
如果想要实现一个宏命令,他的作用是先打开电视再关闭电视。
1 | /** |
1 |
|
1 | public class AClient { |
有没有觉得跟命令对列有点像?都是有一个List对列。
差别在于命令队列,队列是存放在invoker,调用者存放请求者请求过来的命令,可以做限流等操作。
而宏命令,是一个命令的实例,他的队列是存放组成宏的组成。
Reference
https://www.cnblogs.com/JsonShare/p/7206607.html
https://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html