命令模式 (Command Pattern)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化
模式定义
命令模式是一种行为型设计模式,它将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化。命令模式将请求的发送者和接收者解耦,使得系统可以将请求作为对象存储、传递和回调。它也支持撤销操作和队列请求等功能。
模式结构
graph TD
A[Client] --> B((Invoker))
B --> C[Command]
C --> D[Receiver]
实现方式
基础实现
定义命令接口,实现具体命令类、接收者和调用者。
// 命令接口
public interface Command {
void execute();
void undo();
}
// 接收者 - 灯
class Light {
private String location;
public Light(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " 灯打开了");
}
public void off() {
System.out.println(location + " 灯关闭了");
}
}
// 具体命令 - 开灯命令
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[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new Command() {
public void execute() {}
public void undo() {}
};
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
使用示例
public class CommandPatternDemo {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("客厅");
Light kitchenLight = new Light("厨房");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.undoButtonWasPushed();
}
}
经典案例
1. Swing GUI中的Action接口
Java Swing框架广泛使用命令模式。javax.swing.Action接口就是命令模式的一个典型实现:
Action saveAction = new AbstractAction("Save") {
public void actionPerformed(ActionEvent e) {
// 保存文档逻辑
document.save();
}
};
JButton saveButton = new JButton(saveAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);
2. Runnable接口
Java中的Runnable接口也是命令模式的一种体现,线程执行的任务被封装在Runnable对象中:
Runnable task = new Runnable() {
public void run() {
// 执行任务
System.out.println("Task executed");
}
};
Thread thread = new Thread(task);
thread.start();
3. 日志和恢复系统
命令模式可以与备忘录模式结合,实现操作的日志记录和系统恢复功能:
interface Command {
void execute();
void store(); // 存储状态用于恢复
void load(); // 加载之前存储的状态
}
应用场景
- GUI菜单和按钮:将菜单项或按钮点击事件封装为命令对象
- 事务处理系统:将一系列操作封装为命令,支持回滚操作
- 日志系统:将操作记录到日志中,用于系统崩溃后恢复
- 宏命令:将多个命令组合成一个复合命令
- 队列请求:将命令放入队列中异步执行
- 撤销/重做操作:通过undo方法实现撤销功能
优缺点
优点
- 降低耦合度:发送者与接收者之间没有直接引用关系
- 扩展性好:很容易增加新的命令类
- 支持撤销操作:通过undo方法实现命令撤销
- 支持队列请求:可以将命令对象放入队列中延迟执行
- 支持宏命令:可以将多个命令组合成一个复合命令
缺点
- 类数量增加:每增加一个命令就需要增加一个具体命令类
- 系统复杂性增加:引入命令模式会增加系统的复杂性和理解难度