命令模式 (Command Pattern)
什么是命令模式?
命令模式是一种行为型设计模式,它允许你将请求封装为对象,从而可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
简单来说:命令模式就是将"请求"封装成"对象",可以像操作对象一样操作请求。
生活中的例子
想象一下:
- 遥控器:遥控器上的按钮对应不同的命令
- 餐厅点餐:服务员记录订单,厨师按订单做菜
- 宏命令:键盘快捷键执行一系列操作
为什么需要命令模式?
传统方式的问题
java
// 直接调用方法
receiver.action1();
receiver.action2();
问题:
- 无法撤销:无法撤销操作
- 无法记录:无法记录操作历史
- 无法排队:无法对请求排队
- 耦合度高:调用者与接收者耦合
命令模式的优势
java
// 使用命令模式
Command command1 = new ConcreteCommand(receiver);
Command command2 = new ConcreteCommand(receiver);
invoker.setCommand(command1);
invoker.execute();
优势:
- 解耦合:调用者与接收者解耦
- 可撤销:可以撤销操作
- 可记录:可以记录操作历史
- 可排队:可以对请求排队
命令模式的结构
┌─────────────────────┐
│ Command │ 命令接口
├─────────────────────┤
│ + execute(): void │
│ + undo(): void │
└──────────┬──────────┘
│ 实现
│
┌──────────┴──────────┐
│ ConcreteCommand │ 具体命令
├─────────────────────┤
│ - receiver: Receiver│
│ + execute(): void │
│ + undo(): void │
└─────────────────────┘
┌─────────────────────┐
│ Invoker │ 调用者
├─────────────────────┤
│ - command: Command │
│ + setCommand(): void│
│ + execute(): void │
└─────────────────────┘
┌─────────────────────┐
│ Receiver │ 接收者
├─────────────────────┤
│ + action(): void │
└─────────────────────┘
代码示例
1. 定义命令接口
java
/**
* 命令接口
*/
public interface Command {
/**
* 执行命令
*/
void execute();
/**
* 撤销命令
*/
void undo();
}
2. 定义具体命令
java
/**
* 具体命令:开灯
*/
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();
}
}
3. 定义接收者
java
/**
* 接收者:灯
*/
public class Light {
public void on() {
System.out.println("灯打开");
}
public void off() {
System.out.println("灯关闭");
}
}
4. 定义调用者
java
/**
* 调用者:遥控器
*/
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void buttonPressed() {
command.execute();
}
public void undoButtonPressed() {
command.undo();
}
}
5. 使用命令
java
/**
* 命令模式测试类
* 演示如何使用命令模式实现遥控器功能
*/
public class CommandTest {
public static void main(String[] args) {
System.out.println("=== 命令模式测试 ===\n");
// 创建接收者
Light light = new Light();
// 创建命令
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
// 创建调用者
RemoteControl remote = new RemoteControl();
// 测试开灯
System.out.println("--- 开灯 ---");
remote.setCommand(lightOn);
remote.buttonPressed();
// 测试关灯
System.out.println("\n--- 关灯 ---");
remote.setCommand(lightOff);
remote.buttonPressed();
// 测试撤销
System.out.println("\n--- 撤销 ---");
remote.undoButtonPressed();
// 连续操作
System.out.println("\n--- 连续操作 ---");
remote.setCommand(lightOn);
remote.buttonPressed();
remote.buttonPressed();
remote.setCommand(lightOff);
remote.buttonPressed();
System.out.println("\n--- 撤销所有操作 ---");
remote.undoButtonPressed();
remote.undoButtonPressed();
remote.undoButtonPressed();
System.out.println("\n=== 命令模式的优势 ===");
System.out.println("1. 解耦合:调用者与接收者解耦");
System.out.println("2. 可撤销:可以撤销操作");
System.out.println("3. 可记录:可以记录操作历史");
System.out.println("4. 可排队:可以对请求排队");
System.out.println("5. 易于扩展:新增命令很容易");
System.out.println("\n=== 实际应用场景 ===");
System.out.println("1. GUI操作:菜单、按钮操作");
System.out.println("2. 宏命令:键盘快捷键");
System.out.println("3. 事务处理:数据库事务");
System.out.println("4. 撤销重做:文本编辑器的撤销重做");
}
}
命令模式的优点
- 解耦合:调用者与接收者解耦
- 可撤销:可以撤销操作
- 可记录:可以记录操作历史
- 可排队:可以对请求排队
- 易于扩展:新增命令很容易
命令模式的缺点
- 类数量增加:每个命令都需要一个类
- 复杂度增加:引入了额外的类
适用场景
- 需要撤销:需要撤销操作
- 需要记录:需要记录操作历史
- 需要排队:需要对请求排队
- 菜单操作:菜单、按钮等操作
常见应用场景
- GUI操作:菜单、按钮操作
- 宏命令:键盘快捷键
- 事务处理:数据库事务
使用建议
- 需要撤销:使用命令模式
- 需要记录:使用命令模式
- 简单操作:直接调用即可
注意事项
⚠️ 命令模式虽然强大,但要注意:
- 不要过度使用,增加不必要的复杂度
- 考虑使用宏命令