什么是命令模式?
命令模式是一种行为型设计模式,它将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

核心思想: 将“发出请求的对象”(调用者/Invoker)与“接收并执行请求的对象”(接收者/Receiver)解耦,调用者不需要知道接收者的任何细节,它只知道如何调用一个命令对象。
模式核心角色
命令模式主要包含以下几个角色:
- Command (命令接口): 定义了执行操作的接口,这个接口通常只包含一个
execute()方法。 - ConcreteCommand (具体命令): 实现了
Command接口,并持有一个对Receiver对象的引用。execute()方法会调用Receiver的相应操作。 - Receiver (接收者): 知道如何执行与请求相关的具体业务逻辑,是真正干活的对象。
- Invoker (调用者): 要求命令执行请求,它会持有一个
Command对象的引用,并在某个时刻调用其execute()方法,它不关心命令具体是如何实现的。 - Client (客户端): 创建具体的
ConcreteCommand对象,并将其与Receiver和Invoker关联起来。
实例场景:智能家居遥控器
假设我们要为一个智能家居系统设计一个遥控器,这个遥控器有几个按钮,可以控制不同的设备(如电灯、风扇、电视)。
- 请求: 按下遥控器上的按钮。
- 接收者: 电灯、风扇、电视等具体设备。
- 调用者: 遥控器上的按钮。
下面我们用代码来实现这个场景。

步骤 1: 定义 Receiver (接收者)
我们定义具体的设备,它们是真正执行操作的对象。
Light.java (电灯)
// Receiver: 电灯
public class Light {
public void on() {
System.out.println("电灯已打开。");
}
public void off() {
System.out.println("电灯已关闭。");
}
}
Fan.java (风扇)
// Receiver: 风扇
public class Fan {
public void turnOn() {
System.out.println("风扇已开启。");
}
public void turnOff() {
System.out.println("风扇已关闭。");
}
}
步骤 2: 定义 Command 接口 (命令接口)
// Command: 命令接口
public interface Command {
void execute(); // 执行命令
void undo(); // 撤销命令 (可选,但很常用)
}
步骤 3: 创建 ConcreteCommand (具体命令)
我们为每个设备的每个操作创建一个具体的命令类,这些命令会持有对应 Receiver 的引用。

LightOnCommand.java (打开电灯的命令)
// ConcreteCommand: 打开电灯的命令
public 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(); // 撤销操作就是关闭电灯
}
}
LightOffCommand.java (关闭电灯的命令)
// ConcreteCommand: 关闭电灯的命令
public 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();
}
}
FanOnCommand.java (开启风扇的命令)
// ConcreteCommand: 开启风扇的命令
public class FanOnCommand implements Command {
private Fan fan;
public FanOnCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.turnOn();
}
@Override
public void undo() {
fan.turnOff();
}
}
步骤 4: 创建 Invoker (调用者)
遥控器就是调用者,它不需要知道是电灯还是风扇,只需要知道它有一个可以按下的 Command。
RemoteControl.java (遥控器)
// Invoker: 调用者 - 遥控器
public class RemoteControl {
private Command command;
// 设置要执行的命令
public void setCommand(Command command) {
this.command = command;
}
// 按下按钮,执行命令
public void buttonWasPressed() {
command.execute();
}
// 添加一个撤销按钮
public void undoButtonWasPressed() {
command.undo();
}
}
步骤 5: 组装 Client (客户端)
在 main 方法中,我们将所有部分组装起来,让它们协同工作。
SmartHomeDemo.java (客户端)
public class SmartHomeDemo {
public static void main(String[] args) {
// 1. 创建接收者
Light livingRoomLight = new Light();
Fan bedroomFan = new Fan();
// 2. 创建具体的命令对象,并设定它的接收者
Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);
Command fanOn = new FanOnCommand(bedroomFan);
// 3. 创建调用者(遥控器)
RemoteControl remote = new RemoteControl();
System.out.println("--- 场景一:打开客厅的灯 ---");
remote.setCommand(lightOn); // 设置命令
remote.buttonWasPressed(); // 按下执行按钮
System.out.println("\n--- 场景二:打开卧室的风扇 ---");
remote.setCommand(fanOn); // 设置新的命令
remote.buttonWasPressed(); // 按下执行按钮
System.out.println("\n--- 场景三:关闭客厅的灯(撤销操作)---");
remote.setCommand(lightOff); // 设置关闭灯的命令
remote.undoButtonWasPressed(); // 按下撤销按钮
System.out.println("\n--- 场景四:使用宏命令(组合命令)---");
// 宏命令:一个按钮执行多个操作
Command partyOnMacro = new MacroCommand(new Command[]{lightOn, fanOn});
remote.setCommand(partyOnMacro);
remote.buttonWasPressed();
}
}
运行结果:
--- 场景一:打开客厅的灯 ---
电灯已打开。
--- 场景二:打开卧室的风扇 ---
风扇已开启。
--- 场景三:关闭客厅的灯(撤销操作)---
电灯已关闭。
--- 场景四:使用宏命令(组合命令) ---
电灯已打开。
风扇已开启。
命令模式的优点
- 解耦:
Invoker和Receiver完全解耦。Invoker只知道Command接口,不知道具体的Receiver是谁,也不知道它如何工作,这使得我们可以轻松地改变Receiver或添加新的Receiver,而无需修改Invoker的代码。 - 可扩展性: 新增命令非常容易,只需创建一个新的
ConcreteCommand类并实现execute()方法即可,不需要修改现有的Invoker或Receiver代码,这符合“开闭原则”。 - 支持撤销和重做: 通过在
Command接口中增加undo()方法,可以轻松实现撤销操作,历史命令可以被存储在一个堆栈中,实现“重做”功能。 - 支持宏命令: 可以将多个命令组合成一个“宏命令”,一次性执行一系列操作,如上面的例子所示,只需创建一个
MacroCommand即可。 - 支持请求排队和日志: 可以将命令对象放入队列中,实现请求的排队执行,也可以将命令对象持久化,实现请求的日志记录,在系统崩溃后可以重新执行这些命令。
适用场景
- 当你需要将调用操作的对象与知道如何执行该操作的对象解耦时。
- 当你想要支持撤销操作时。
- 当你想要将操作组合成宏命令时。
- 当你需要支持日志记录,并允许在系统崩溃后恢复操作时。
- 当你想要实现事务性操作时(一组命令要么全部成功,要么全部回滚)。
命令模式通过将请求封装成对象,极大地增强了系统的灵活性、可扩展性和可维护性。
