一、模式介绍
1.1、定义
将请求封装成一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象中),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。
命令模式的主要作用和应用场景,是用来控制命令的执行,比如:异步、延迟、排队执行命令、撤销重做命令、给命令记录纪录日志等。
1.2、优点
- 通过引入中间件(抽象接口)降低系统的耦合度
- 扩展性良好,增加或删除命令非常方便。采用命令模式增加或删除命令不会影响其他类,满足开闭原则
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令
- 方便实现 Undo 和 Redo 操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复
- 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活
1.3、缺点
- 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性
- 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的难度
二、结构与实现
2.1、结构
- 抽象命令角色(Command):声明执行命令的接口,拥有执行命令的抽象方法 execute
- 具体命令角色(ConcreteCommand):是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作
- 实现者/接收者角色(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者
- 调用者/请求者角色(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关的请求,它不直接访问接收者
2.2、实现
2.2.1、类图
2.2.2、LightCommand
package com.erlang.command;
/**
* @description: 抽象命令
* @author: erlang
* @since: 2022-02-16 23:38
*/
public interface LightCommand {
/**
* 执行命令方法
*/
void execute();
}
2.2.3、LightOffCommand
package com.erlang.command;
/**
* @description: 关闭命令
* @author: erlang
* @since: 2022-02-16 23:46
*/
public class LightOffCommand implements LightCommand {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
2.2.4、LightOnCommand
package com.erlang.command;
/**
* @description: 打开命令
* @author: erlang
* @since: 2022-02-16 23:46
*/
public class LightOnCommand implements LightCommand {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
2.2.5、NoLightCommand
package com.erlang.command;
/**
* @description: 空命令
* @author: erlang
* @since: 2022-02-16 23:52
*/
public class NoLightCommand implements LightCommand {
@Override
public void execute() {
System.out.println("NoLightCommand 空命令");
}
}
2.2.6、Light
package com.erlang.command;
/**
* @description: 接收者角色
* @author: erlang
* @since: 2022-02-16 23:46
*/
public class Light {
/**
* 名称
*/
private String name;
public Light(String name) {
this.name = name;
}
/**
* 打开
*/
public void on() {
System.out.printf("打开%s\n", name);
}
/**
* 关闭
*/
public void off() {
System.out.printf("关闭%s\n", name);
}
}
2.2.7、RemoteControl
package com.erlang.command;
import java.util.Arrays;
/**
* @description: 遥控器
* @author: erlang
* @since: 2022-02-16 23:38
*/
public class RemoteControl {
LightCommand[] onCommands;
LightCommand[] offCommands;
public RemoteControl() {
onCommands = new LightCommand[7];
offCommands = new LightCommand[7];
LightCommand lightCommand = new NoLightCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = lightCommand;
offCommands[i] = lightCommand;
}
}
public void setCommand(int index, LightCommand on, LightCommand off) {
onCommands[index] = on;
offCommands[index] = off;
}
public void onButtonWasPush(int index) {
onCommands[index].execute();
}
public void offButtonWasPush(int index) {
offCommands[index].execute();
}
}
2.2.8、RemoteLoader
package com.erlang.command;
/**
* @description: 命令模式客户端测试
* @author: erlang
* @since: 2022-02-16 23:37
*/
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
LightCommand tvOn = new LightOnCommand(new Light("电视"));
LightCommand tvOff = new LightOffCommand(new Light("电视"));
remoteControl.setCommand(0, tvOn, tvOff);
LightCommand airOn = new LightOnCommand(new Light("空调"));
LightCommand airOff = new LightOffCommand(new Light("空调"));
remoteControl.setCommand(1, airOn, airOff);
System.out.println("操作电视:");
remoteControl.onButtonWasPush(0);
remoteControl.offButtonWasPush(0);
System.out.println("-----------------------------\n操作空调:");
remoteControl.onButtonWasPush(1);
remoteControl.offButtonWasPush(1);
System.out.println("-----------------------------\n空操作:");
remoteControl.onButtonWasPush(2);
remoteControl.offButtonWasPush(2);
}
}
2.2.9、执行结果
操作电视:
打开电视
关闭电视
-----------------------------
操作空调:
打开空调
关闭空调
-----------------------------
空操作:
NoLightCommand 空命令
NoLightCommand 空命令