定义
将请求封装为一个对象,从而使你能够参数化其他对象的方法调用、在不同的时间安排或队列请求、支持可撤销操作以及提供对请求历史的记录。这种模式使得系统中的对象可以在接收请求和执行请求之间解耦。
优缺点
优点:
- 解耦请求发送者和接收者:通过引入命令对象,使得调用者与接收者之间解耦。调用者只需知道如何执行命令,而不必了解命令的具体实现细节。
- 增加/修改新的命令方便:由于每个具体命令都是一个独立的类,添加新的命令或者修改现有命令的行为只需要新增或修改相应的命令类即可,对其他部分代码无影响。
- 支持可撤销操作:命令模式能够轻松实现命令的撤销功能,只需在命令类中存储执行前的状态,并提供 undo() 方法即可回滚到之前的状态。
- 命令队列和日志:可以将多个命令对象放入队列中进行排队执行,也可以记录命令的历史以提供后期分析或恢复系统状态。
- 职责链模式的基础:命令模式可以作为构建职责链模式的基础,让一个请求沿着责任链传递,直到被某个处理者对象处理为止。
缺点:
- 可能产生大量命令类:对于具有大量操作需要封装为命令的情况,可能会导致命令类数量较多,增加系统的复杂性。
- 过度设计:如果系统中的命令种类不多,或者命令的执行逻辑非常简单,采用命令模式可能会显得过于复杂,增加了额外的抽象层次。
- 命令对象的生命周期管理:如果命令对象中包含有状态信息,在执行完命令后,需要妥善管理这些对象的生命周期,防止内存泄漏等问题。
- 性能开销:创建和维护命令对象以及执行命令时,特别是涉及到撤销操作和命令历史记录时,会有一定的性能开销。
结构图和示例
- Command Interface 命令接口:定义了一个所有具体命令类都要实现的接口,通常包含一个execute() 方法,该方法用于执行请求。
- ConcreteCommand 具体命令:实现了命令接口的具体类,包含了执行请求所需的所有信息,包括接收者对象和相关的数据。当命令被执行时,具体命令对象会调用接收者的特定方法来完成请求。
- Receiver 接收者:接收并执行请求的对象。任何类都可以作为接收者,只要它提供了命令需要调用的方法。
- Invoker/Client 调用者/客户端:创建命令对象并负责调用其 execute() 方法的角色。调用者并不知道具体的命令如何实现,只知道它可以执行或撤销。
- 调用者可以持有命令列表:在某些情况下,调用者可以存储一系列命令,然后按顺序执行他们,或者允许用户通过回滚操作撤销先前的命令。
// 接收者角色:灯
class Light {
public void turnOn() {
System.out.println("Light is on.");
}
public void turnOff() {
System.out.println("Light is off.");
}
}
// 命令接口
interface Command {
void execute();
}
// 具体命令角色:打开灯的命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
// 具体命令角色:关闭灯的命令
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
// 调用者角色:遥控器
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
// 创建接收者对象
Light light = new Light();
// 创建具体的命令对象
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
// 创建调用者对象并设置命令
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOnCommand);
// 按下遥控器按钮,执行命令
remote.pressButton(); // 输出 "Light is on."
// 改变命令
remote.setCommand(lightOffCommand);
// 再次按下遥控器按钮,执行新的命令
remote.pressButton(); // 输出 "Light is off."
}
}