命令
更新: 6/22/2025 字数: 0 字 时长: 0 分钟
定义
命令模式 (Command Pattern) 将请求封装为对象,使你可以参数化客户端请求、队列请求、记录请求日志,以及支持可撤销操作。它解耦了请求发送者和接收者,使系统更灵活可扩展。
角色
- 命令 (Command):声明执行操作的接口
- 具体命令 (Concrete Command):将接收者绑定到动作
- 调用者 (Invoker):要求命令执行请求
- 接收者 (Receiver):知道如何执行操作
- 客户端 (Client):创建具体命令并设置接收者
实现
基础命令模式
java
// 命令接口
public interface Command {
void execute();
void undo();
}
// 接收者:灯
public class Light {
public void on() {
System.out.println("灯已打开");
}
public void off() {
System.out.println("灯已关闭");
}
}
// 具体命令:开灯命令
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();
}
}
// 具体命令:关灯命令
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();
}
}
// 调用者:遥控器
public class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
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();
}
}
// 空命令(避免 null 检查)
public class NoCommand implements Command {
@Override
public void execute() {}
@Override
public void undo() {}
}
// 客户端使用
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remote = new RemoteControl();
// 创建接收者
Light livingRoomLight = new Light();
// 创建命令
LightOnCommand livingRoomLightOn =
new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff =
new LightOffCommand(livingRoomLight);
// 设置命令到遥控器
remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
// 测试
remote.onButtonWasPushed(0);
remote.offButtonWasPushed(0);
System.out.println("-- 撤销操作 --");
remote.undoButtonWasPushed();
}
}
宏命令(命令组合)
java
// 宏命令:一次执行多个命令
public class MacroCommand implements Command {
private Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
}
}
@Override
public void undo() {
// 逆序撤销
for (int i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
}
}
// 使用示例
public class MacroCommandDemo {
public static void main(String[] args) {
// 创建接收者
Light light = new Light();
TV tv = new TV();
// 创建命令
LightOnCommand lightOn = new LightOnCommand(light);
LightOffCommand lightOff = new LightOffCommand(light);
TVOnCommand tvOn = new TVOnCommand(tv);
TVOffCommand tvOff = new TVOffCommand(tv);
// 创建宏命令
Command[] partyOn = { lightOn, tvOn };
Command[] partyOff = { lightOff, tvOff };
MacroCommand partyOnMacro = new MacroCommand(partyOn);
MacroCommand partyOffMacro = new MacroCommand(partyOff);
// 使用宏命令
RemoteControl remote = new RemoteControl();
remote.setCommand(0, partyOnMacro, partyOffMacro);
System.out.println("--- 执行宏命令开 ---");
remote.onButtonWasPushed(0);
System.out.println("--- 执行宏命令关 ---");
remote.offButtonWasPushed(0);
System.out.println("--- 撤销操作 ---");
remote.undoButtonWasPushed();
}
}
优缺点
优点:
- 解耦调用者和接收者
- 支持撤销/重做操作
- 支持命令组合(宏命令)
- 支持请求排队和日志记录
- 易于扩展新命令
- 实现请求延迟执行
缺点:
- 增加系统复杂度(多个新类)
- 命令对象可能过多
- 增加内存开销
- 实现完备的撤销/重做机制较复杂
应用场景
- GUI 按钮和菜单项
- 事务处理系统
- 多级撤销/重做功能
- 任务调度系统
- 日志记录系统
- 宏录制功能
- 异步任务队列
与其他模式的关系
- 与责任链模式:命令可组成责任链
- 与备忘录模式:配合实现撤销功能
- 与原型模式:用于命令的复制
- 与策略模式:命令对象可包含策略
- 与组合模式:宏命令是组合模式的应用
- 与观察者模式:命令执行可触发通知
JDK 中的命令模式
- Java 线程(Runnable 接口)
- Swing 的 Action 接口
- Java 定时器(TimerTask)
- Java 事务 API(JTA)
- Java 反射(Method.invoke)
- Lambda 表达式(函数式接口)
- Spring 的 CommandLineRunner
注意事项
- 命令粒度设计(避免过细或过粗)
- 命令对象生命周期管理
- 线程安全问题(共享命令状态)
- 撤销/重做实现策略
- 命令持久化设计
- 空命令处理(避免 null 检查)
- 命令参数化设计