问题背景
1)随着现在科技越来越先进,我们在家庭中对物品的开关都不需要亲自走过去来进行了。我们只需要通过手机APP中的按键来远程执行这个命令。
2)其实这就是命令模式,使用者完全不需要懂这个命令如何执行,谁来执行,使用者只需要发送命令即可。
3)命令模式可将“动作的请求者”从“动作的执行者对象中解耦出来
命令模式
基本介绍
1)命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道彼请求的操作是哪个
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2)命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3)在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
4)通俗易懂的理解,将军发布命令,士兵去执行。其中有几个角色,将军(命令发布者)、士兵 (命令的具体执行者) 、命令(连接将军和士兵)。
Invoker是调用者 (将军),Receiver是被调用者 (士兵),MyCommand是命令,实现了Command接口,持有接收对象
5)命令的发送者完全不需知道谁来执行,只需要发送
UML类图
1)Invoker是调用者角色
2)Command是命令调用者,需要执行所有命令都在这里,可以是接口或者抽象类
3)Receiver是接收者,具体执行命令的角色
4)ConcreteCommand将一个接收者对象与一个动作绑定,调用接收者相应的操作
5)不理解可结合实际问题解决得UML类图来理解
解决方案
UML类图
1)原始物品LightReceiver有开关方法
2)继承Command接口创建等得开关命令,开灯命令撤回方法就是关灯,关灯方法撤回方法就是开灯
3)创建一个RemoteController来保存这些命令,调用得时候直接调用RemoteController类即可
4)NoCommand是一个空命令,可以简化我们的判空操作
5)RemoteController类中有两个属性,map用来保存命令,stack用来记录执行的命令
代码示例
/**
* 灯
*
* @author wenqiang
* @date 2023/6/1
*/
public class LightReceiver {
public void on() {
System.out.println("开灯");
}
public void off() {
System.out.println("关灯");
}
}
/**
* 命令接口
*
* @author wenqiang
* @date 2023/6/1
*/
public interface Command {
/**
* 执行命令
*/
void execute();
/**
* 撤回命令
*/
void undo();
}
/**
* 开灯命令
*
* @author wenqiang
* @date 2023/6/1
*/
public class LightOnCommand implements Command {
private LightReceiver lightReceiver = new LightReceiver();
@Override
public void execute() {
lightReceiver.on();
}
@Override
public void undo() {
lightReceiver.off();
}
}
/**
* 关灯命令
*
* @author wenqiang
* @date 2023/6/1
*/
public class LightOffCommand implements Command {
private LightReceiver lightReceiver = new LightReceiver();
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
/**
* 空命令
*
* @author wenqiang
* @date 2023/6/1
*/
public class NoCommand implements Command {
@Override
public void execute() {
System.out.println("空命令");
}
@Override
public void undo() {
System.out.println("空命令");
}
}
/**
* 控制界面
*
* @author wenqiang
* @date 2023/6/1
*/
public class RemoteController {
/**
* 用来保存命令的设置信息
*/
private Map<String, Command> map = new HashMap<>(8);
/**
* 用来保存执行的命令 【使用栈】 【用于命令撤回】
*/
private Stack<Command> stack = new Stack<>();
/**
* 设置命令
*
* @param name
* @param command
*/
public void setCommond(String name, Command command) {
map.put(name, command);
}
/**
* 开
*
* @param name
*/
public void on(String name) {
Command command = map.getOrDefault(name, new NoCommand());
command.execute();
stack.push(command);
}
/**
* 关
*
* @param name
*/
public void off(String name) {
Command command = map.getOrDefault(name, new NoCommand());
command.execute();
stack.push(command);
}
/**
* 撤回
*
*/
public void undo() {
if (stack.isEmpty()) {
new NoCommand().undo();
return;
}
Command pop = stack.pop();
pop.undo();
}
}
我们来设置命令并使用他们
public class Client {
public static void main(String[] args) {
// 创建一个控制界面
RemoteController remoteController = new RemoteController();
// 设置命令
remoteController.setCommond("灯-开", new LightOnCommand());
remoteController.setCommond("灯-关", new LightOffCommand());
// 执行命令
remoteController.on("灯-开");
// 回撤命令
remoteController.undo();
remoteController.undo();
}
}
执行结果
我们来增加一种设备,加入到控制界面
/**
* 电视
*
* @author wenqiang
* @date 2023/6/1
*/
public class TelevisionReceiver {
public void on() {
System.out.println("打开电视");
}
public void off() {
System.out.println("关闭电视");
}
}
/**
* 打开电视命令
*
* @author wenqiang
* @date 2023/6/1
*/
public class TelevisionOnCommand implements Command {
private TelevisionReceiver televisionReceiver = new TelevisionReceiver();
@Override
public void execute() {
televisionReceiver.on();
}
@Override
public void undo() {
televisionReceiver.off();
}
}
/**
* 关闭电视命令
*
* @author wenqiang
* @date 2023/6/1
*/
public class TelevisionOffCommand implements Command {
private TelevisionReceiver televisionReceiver = new TelevisionReceiver();
@Override
public void execute() {
televisionReceiver.off();
}
@Override
public void undo() {
televisionReceiver.on();
}
}
将新设备的命令增加到控制界面
public class Client {
public static void main(String[] args) {
// 创建一个控制界面
RemoteController remoteController = new RemoteController();
// 设置命令
remoteController.setCommond("灯-开", new LightOnCommand());
remoteController.setCommond("灯-关", new LightOffCommand());
remoteController.setCommond("电视-开", new TelevisionOnCommand());
remoteController.setCommond("电视-关", new TelevisionOffCommand());
// 执行命令
remoteController.on("灯-开");
remoteController.on("电视-开");
remoteController.on("电视-关");
// 回撤命令
remoteController.undo();
remoteController.undo();
}
}
执行结果