介绍
命令模式(Command Pattern)是一种行为设计模式,允许将请求(命令)封装为对象,从而使您可以使用不同的请求、队列或记录请求日志,以及支持可撤销操作。
1. 定义
命令模式将一个请求封装为一个对象,从而能够参数化其他对象以便于请求的发起、排队或日志记录。
2. 主要作用
- 将请求调用者与请求接收者解耦。
- 支持请求的排队和日志记录。
- 支持可撤销操作。
3.解决的问题
命令模式主要解决的是请求的发送者和接收者之间的紧耦合。通过将请求封装为对象,命令模式能够灵活地设计和扩展请求的处理。
4.模式原理
包含角色:
- 命令接口(Command): 定义执行命令的接口。
- 具体命令类(ConcreteCommand): 实现命令接口,定义与接收者之间的绑定。
- 接收者(Receiver): 知道如何执行与某个请求相关的操作。
- 调用者(Invoker): 持有命令对象并在需要时调用它。
UML类图:
示例代码:
通过Command
接口和具体命令类实现了对请求的封装,RemoteControl
类可以通过调用不同的命令来触发不同的行为。
// 命令接口
interface Command {
void execute();
void undo();
}
// 命令接收者
class Light {
public void on() {
System.out.println("Light is ON");
}
public void off() {
System.out.println("Light is OFF");
}
}
// 具体命令类
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
// 命令调用者
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
调用
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();
remote.pressUndo();
remote.setCommand(lightOff);
remote.pressButton();
remote.pressUndo();
}
}
打印输出
Light is ON
Light is OFF
Light is OFF
Light is ON
命令模式可能理解起来稍微麻烦一些,毕竟角色比较多,将示例代码和上面UML类图结合起来看,会更容易理解。
看到这个模式,让我想起了之前做过的一个项目,需要上位机和下位机或蓝牙模块不停的进行通信,接收和发送实时数据,指令要根据具体协议进行传输,都是字节数组,指令是按帧进行传输的,一条指令本身包括长度,校验和,头字节,尾字节等关键信息,附加的信息还有发送间隔,指令类型如485 或 232等,为了统一管理这些指令,就统一提取了命令接口。
为了防止指令发送顺序,使用到了队列,不停地喂数据,发送线程不停地取数据,这里的数据就是一个一个的具体命令对象,将命令取出来后,根据其内部信息,再判断是向下位机发送还是蓝牙模块发送,突然发现命令模式好像专门因这个场景而诞生的,哈哈哈。
多说两句,管理和发送不同协议的指令还不算太难,难的是通过一个数据接收类,统一接收并区别不同的指令,然后在不同的地方处理才是最难的,少则有两个不同协议,多则三个或以上,它们发过来都是一段字节数据,有可能是完整的指令,有可能只是一段指令,你不知道它是哪个协议的,只能将数据放到一个很大的字节数组中,供后续拼接,然后再不停的校验,而且要做到毫秒级提取,慢一点存放字节容器就会爆满,每种协议都有自己单独的校验方式,奇校验、偶校验、累加和,异或… 光是听到都头大,最后将领导写的初版代码,优化、验证、重写了无数遍,才完全理解,最后将领导的代码删了,使用了我的版本(可以平替,支持更多校验方式,更容易理解的代码),哈哈哈哈,一个能吹nb的功能。
说的有点多了,但那段回忆,一下就上来了,不吐不快。
5.优缺点
优点:
- 降低了系统的耦合度。
- 增强了系统的灵活性和可扩展性。
- 可以很方便地添加新的命令。
缺点:
- 需要创建许多类,增加了系统的复杂性。
- 如果命令过多,可能会导致系统的可维护性降低。
6.应用场景
- 界面菜单系统。
- 实现Undo/Redo功能。
- 请求需要排队的场景。
7.总结
命令模式通过将请求封装为对象,提供了灵活的请求处理方式。它有助于实现请求的解耦、可撤销操作和请求日志。尽管它增加了类的数量,但在需要复杂请求处理的系统中,它的优势是显而易见的。