命令模式
业务需求
- 智能生活项目需求
- 有一套只能家电,有照明灯,风扇,冰箱,洗衣机,我们只需要手机安装app即可控制这些家电
- 这些智能家电来自不同的厂家,不想每个家电都安装app分别控制,希望只安装一个app就可以控制全部智能家电
- 要实现一个app控制所有只能家电的需要,需要每个只能家电提供一个统一的接口调用,适合使用命令模式
- 命令模式可以将动作的请求者,从动作的执行者对象汇总解耦出来
- 本例中,动作请求者是手机app,都做执行者是每个厂商的一个家电产品
基本介绍
- 命令模式,在设计软件中,我们经常需要向某些对象发送请求,但是不知道具体请求接收是谁,也不知道被请求操作的是哪个;只需在程序运行时指定具体的接受者即可,使用命令模式设计
- 命令模式使请求发送者与请求接受者消除彼此耦合,让对象之间调用关系更加灵活
- 在命令模式中,会将一个请求封装为一个对象,便于使用不同参数来表示不同请求,命令模式也支持撤销操作
- 通俗理解,长官发出命令,士兵去执行;长官是调用者Invoke,士兵是接受者Receiver,MyCommond是命令,实现了Commond接口,持有接收对象
原理类图
原理类图说明
- Invoker:调用者角色
- Command:命令角色,包含需要执行的所有命令,可以是接口或抽象类
- Receiver:接收者角色,知道如何实施和执行一个请求相关操作
- ConcreteCommand:将一个接收者对象和一个动作绑定,调用接收者相应的操作,实现excute
代码实现
定义接收者,具体执行功能
public class LightReceiver {
public void on() {
System.out.println("电灯打开了...");
}
public void off() {
System.out.println("电灯关闭了...");
}
}
命令接口
/ 命令接口
public interface Command {
// 执行动作
public void excute();
// 撤销动作
public void undo();
}
开命令
public class LightOnCommand implements Command {
//聚合LightReceiver
LightReceiver light;
// 构造
public LightOnCommand(LightReceiver light) {
this.light = light;
}
@Override
public void excute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
关命令
public class LightOffCommand implements Command {
//聚合LightReceiver
LightReceiver light;
// 构造
public LightOffCommand(LightReceiver light) {
this.light = light;
}
@Override
public void excute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
空命令
// 空执行,用于初始化
public class NoCommand implements Command{
@Override
public void excute() {
// 空操作
}
@Override
public void undo() {
// 空操作
}
}
遥控器,整合接收者和命令
public class RemoteController {
//开 按钮命令组
Command[] onCommands;
Command[] offCommands;
// 记住最近的一次操作,用于撤销
Command undoCommand;
// 构造器,完成按钮初始化
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
// 用空操作初始化
for (int i=0; i<5;i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
// 设置需要的命令
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开按钮
public void onButtonWasPushed(int no) {
// 找到对应按钮,调用对应方法
onCommands[no].excute();
// 记录这次操作,用于撤销
undoCommand = onCommands[no];
}
// 按下关按钮
public void offButtonWashPushed(int no) {
offCommands[no].excute();
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
客户端
public class Client {
public static void main(String[] args) {
// 接收者
LightReceiver lightReceiver = new LightReceiver();
// 开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 遥控器
RemoteController remoteController = new RemoteController();
// 设置命令
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("按下开灯按钮===============");
remoteController.onButtonWasPushed(0);
System.out.println("按下关灯按钮===============");
remoteController.onButtonWasPushed(0);
System.out.println("撤销=====================");
remoteController.undoButtonWasPushed();
}
}
说明
- 如果扩展新的功能,需要新增新的接收者、开命令、关命令;
- 空命令可以保存最近的一次命令,方便随时撤销最近的命令,也就是执行最近命令的相反命令
- 结构略复杂,实质就是命令类聚合了接收者,遥控器聚合了命令类
Spring源码分析—JdbcTemplate
命令模式注意事项
- 将发请求的对象和执行请求的对象解耦;命令对象负责纽带桥梁作用,请求发起者和请求执行者之间的解耦通过命令对象实现
- 容易设计一个命令队列,把命令放到队列中,可以多线程执行命令
- 容易实现对请求的撤销和重做
- 命令模式不足,造成系统有过多的类,增加系统复杂度
- 空命令也是一种设计模式,为我们省去了判断空的操作;如果没有空命令,可能每按下按键都要判断空,造成编码麻烦
- 命令模式经典应用场景,界面的一个个按钮命令,模拟CMD订单的撤销/回复,触发-反馈机制