基本概念
命令模式 command pattern ,将请求封装成一个命令对象,传递给调用者,调用者找到具体可以处理该命令的对象,由该对象去执行具体逻辑。使得请求发送者和请求的接受者彼此解耦,使得对象的调用关系更加灵活。
uml
角色分析
Invoker 调用者 命令发起者 类似一个总控
Command 命令抽象接口 包含执行和撤销操作
Receiver 接受者被聚合到具体的命令中执行具体的逻辑
ConcreteCommand 将命令和具体接受者的动作绑定实现execute和undo逻辑
代码实现
现在有一套智能家居需要控制,我们抽取一个通用调控器(Invoker)来控制各个家居(Receiver)的开关功能(ConcreteCommand)。
public interface Command {
/**
* 执行命令
*/
void execute();
/**
* 撤销操作
*/
void undo();
}
// Recevier 具体命令的执行者
public class LightReceiver {
public void on(){
System.out.println("灯开了");
}
public void off(){
System.out.println("灯关了");
}
}
// 灯打开的命令
public class LightOnCommand implements Command {
// 将灯的具体操作接受者聚合到命令中
private final LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
// 打开
lightReceiver.on();
}
@Override
public void undo() {
// 关闭
lightReceiver.off();
}
}
// 灯关闭
public class LightOffCommand implements Command {
private final LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
// 调用灯关闭
lightReceiver.off();
}
@Override
public void undo() {
// 灯关闭的取消就是打开
lightReceiver.on();
}
}
// 空命令 主要为了避免空指针
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
// Invoker对象
public class RemoteInvoker {
// 打开命令数组
Command[] onCommands = new Command[5];
// 关闭命令数组
Command[] offCommands = new Command[5];
// 上一个执行命令 用于执行undo
Command preCommand = new NoCommand();
// 初始化全部为空命令 避免了非空判断
public RemoteInvoker() {
// 初始化
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
/**
* 设置命令
* @param index 命令存在位置 相当于遥控器上面的哪一个按钮
* @param isOn 是不是开命令
* @param command 具体哪个命令
*/
public void setCommand(int index, boolean isOn, Command command) {
if (isOn) {
onCommands[index] = command;
} else {
offCommands[index] = command;
}
}
/**
* 按开命令
* @param index 哪一个命令
*/
public void pressOffButton(int index) {
offCommands[index].execute();
preCommand = offCommands[index];
}
/**
* 按关闭命令
* @param index
*/
public void pressOnButton(int index){
onCommands[index].execute();
preCommand = onCommands[index];
}
/**
* 取消上一次操作
*/
public void undo(){
preCommand.undo();
}
}
// 使用测试
public class CommandTest {
public static void main(String[] args) {
// 先创建接受者
LightReceiver lightReceiver = new LightReceiver();
// 创建命令 聚合就饿守着
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 创建Invoker 命令执行者
RemoteInvoker remoteInvoker = new RemoteInvoker();
// 设置命令
remoteInvoker.setCommand(0, true, lightOnCommand);
remoteInvoker.setCommand(0, false, lightOffCommand);
//调用执行
remoteInvoker.pressOnButton(0);
remoteInvoker.pressOffButton(0);
// 撤销操作
remoteInvoker.undo();
}
}
/** 最终输出
灯开了
灯关了
灯开了
*/
使用细节
命令模式主要想把请求的对象和执行请求的对象进行解耦,调用者只需要调用命令对象的execute方法,就可以让接受者工作,无需知道具体的接受者是谁,如何实现。本质上执行工作其实是接受者,命令对象起到一个桥梁的作用。
命令模式容易实现对请求的撤销操作,空命令可以为我们省略非空判断。
命令模式可能导致许多的命令类,不同的操作对应一个命令类,导致命令类过多增加的业务的复杂的。