作用
把方法调用封装起来,当需要将发出请求的对象与执行请求的对象解耦时,使用命令模式。将请求封装成对象,可以让你自由的使用不同的请求,队列,支持将命令组装。
角色
在命令模式中,有这个几个角色是构成此模式的重点
- Client:客户,用来发起请求,创建具体的Command对象,并在其构造方法中设置接收者
- Command:命令对象,一般先定义Command接口,具体实现继承Command接口
- ConcreteCommand:命令的具体实现,在对象中需要注入接收者,执行execute方法时调用接收者的实际执行方法
- Invoker:调用者,用来将命令从客户传递给接收者的中间类,提供setCommand方法接收命令,并调用Command.execute方法发起请求
- Receiver:接收者,真正的动作执行者
类图
类比助记
结合Head first中的例子,来理解记忆
- 顾客到餐厅来点餐 :客户负责创建命令对象,命令对象包含了包含了接受者上的一组动作,createCommandObject()
- 服务员过来等待客人点餐:创建Invoker对象,等待Client创建命令对象
- 在菜单上写下想吃的菜:调用Invoker.setCommand()方法给Invoker对象的Command属性赋值
- 服务员拿走订单并通知后厨:Invoker 对象实际调用command.execute()方法
- 厨师接到通知开始炒菜:Receiver对象收到Invoker的调用请求,开始执行action()方法,实际的逻辑操作
通过以上五步就完成了命令模式的整个执行过程
使用方式
这里介绍命令模式一般可以做什么事情
1. 单个的命令操作
命令模式最简单的实现,比如灯泡有开和关两种状态
那么就可以定义两个命令LightOnCommand,LightOffCommand 类
调用时直接调用命令对象的execute()方法即可
2. 宏命令
当单个的命令不再满足你的要求时,可以使用宏命令来解决
比如现在的智能家居,你到家之后不仅需要开灯,还要打开空调,电脑,窗帘等等设备
实现起来也很简单,就是将已经定义好的多个Command实现类组装成一个集合
这里调用者中的command属性就变成了command[]集合了
然后重新定义一个Command对象,execute方法中遍历执行上一步定义好的集合
这回再调用时就可以调用一个Command对象来执行多个操作步骤
3. 对列请求
这个也很好理解,就是你有很多个命令组成队列
使用线程池来执行队列中的命令,来多线程执行
4. 日志请求
这里就需要改造下Command接口了,加上store()和load()方法用来保存和加载
使用store()命令都保存在日志或者磁盘中,当执行过程中系统死机恢复后,调用load()方法来重新加载这一批命令并重新执行
这个要么全部执行,要么都不执行的操作再事物中也有使用
5.撤销操作
命令模式支持撤销操作,具体如下
在命令接口中增加undo()方法
并在调用者对象中增加undoCommand属性用来记录前一次命令
当调用撤销时,调用者紧接着调用undoCommand的undo()方法
延伸:这里的撤销操作实际上也是一种备忘录模式,可见设计模式之间也是互相渗透,重点在与思想,而非手法
要点
- 命令模式将发出请求的对象和执行者解耦
- 再被解耦的两者之间时通过命令对象来进行沟通的,命令对象封装了接收者和一个或一组动作
- 调用者通过调用命令对象的execute()发出请求,这会是的接受者的动作被调用
- 调用者可以接受命令参数,甚至再运行时动态的进行
- 命令可以支持撤销,做法是实现一个undo()方法来回到execute执行前的状态
- 宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也支持撤销
- 实际操作中,很常见使用“聪明”命令对象(再execute方法中不光是调用,也有一些逻辑),也就是直接实现了请求,而不是将工作委托给接收者
- 命令也可以用来实现日志和事物
示例代码
- 创建抽象命令类
public interface Command {
void execute();
}
- 创建Reciver
public class Light {
public void on(){
System.out.println("light on");
}
public void off() {
System.out.println("light off");
}
}
- 创建命令实现类,电灯类有开关两个操作,所以创建两个命令实现类,这里将Reveiver作为命令参数
public class LightOffCommand implements Command{
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
- 创建Invoker,也就是服务员,buttonWasPressed方法就是调用者的调用操作
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
- 简单实现一个开灯操作
public static void main(String[] args) {
// 调用者-服务员
SimpleRemoteControl control = new SimpleRemoteControl();
// 接收者,实际实现功能的人-厨师
Light light = new Light();
// 命令对象-顾客点餐,创建命令对象,并加入接受者
LightOnCommand order = new LightOnCommand(light);
// 将命令传给调用者-顾客将订单给服务员
control.setCommand(order);
// 调用者请求接受者-服务员让厨师炒菜
control.buttonWasPressed();
}
- 运行结果