1. 命令模式的核心思想与应用场景
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成对象,从而允许用户使用不同的请求、队列或日志来参数化其他对象。这种模式的核心在于解耦"请求发送者"和"请求接收者",让两者不直接交互,而是通过命令对象进行中介。
在实际开发中,命令模式最常见的应用场景包括:
- 需要将操作请求与执行操作的对象解耦时
- 需要支持命令的撤销(Undo)和重做(Redo)功能时
- 需要将命令排队、记录命令历史或支持事务性操作时
- 需要实现回调机制或延迟执行时
提示:命令模式特别适合处理需要"回放"或"撤销"的操作序列,比如图形编辑器的操作历史、游戏中的回放系统等。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 命令模式的结构与组件
2.1 标准UML类图解析
一个典型的命令模式实现包含以下核心组件:
- Command(命令接口):声明执行操作的接口,通常只包含一个execute()方法
- ConcreteCommand(具体命令):实现命令接口,绑定接收者与动作
- Invoker(调用者):要求命令执行请求
- Receiver(接收者):知道如何执行与请求相关的操作
- Client(客户端):创建具体命令对象并设置其接收者
java复制// 命令接口
public interface Command {
void execute();
void undo();
}
// 具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
// 接收者
public class Light {
public void on() {
System.out.println("Light is on");
}
public void off() {
System.out.println("Light is off");
}
}
// 调用者
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
2.2 各组件职责详解
**Receiver(接收者)**是真正执行命令的对象,它知道如何完成具体的业务逻辑。在上面的例子中,Light类就是接收者,它知道如何打开和关闭灯光。
**Command(命令接口)**定义了执行操作的统一接口,通常包含execute()方法,有时还会包含undo()方法以支持撤销操作。
**ConcreteCommand(具体命令)**是命令接口的实现类,它将接收者对象与动作绑定在一起。当调用execute()方法时,具体命令会调用接收者的相应方法。
**Invoker(调用者)**持有命令对象,并在某个时间点调用命令对象的execute()方法。调用者不需要知道命令的具体实现,只需要知道命令接口。
**Client(客户端)**负责创建具体命令对象,并设置命令的接收者。客户端将命令对象传递给调用者,由调用者在适当的时候执行命令。
3. 命令模式的Java实现示例
3.1 智能家居控制系统案例
让我们通过一个更完整的智能家居控制系统示例来演示命令模式的实现:
java复制// 命令接口
public interface Command {
void execute();
void undo();
}
// 具体命令 - 开灯命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
// 具体命令 - 关灯命令
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.off();
}
public void undo() {
light.on();
}
}
// 具体命令 - 调高音响音量
public class StereoVolumeUpCommand implements Command {
private Stereo stereo;
public StereoVolumeUpCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.volumeUp();
}
public void undo() {
stereo.volumeDown();
}
}
// 接收者 - 灯光
public class Light {
private String location;
public Light(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " light is on");
}
public void off() {
System.out.println(location + " light is off");
}
}
// 接收者 - 音响
public class Stereo {
private int volume = 5; // 默认音量
public void volumeUp() {
volume++;
System.out.println("Stereo volume is now " + volume);
}
public void volumeDown() {
if (volume > 0) {
volume--;
System.out.println("Stereo volume is now " + volume);
}
}
}
// 调用者 - 遥控器
public class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteControl() {
onCommands = new Command[2];
offCommands = new Command[2];
// 初始化时设置空命令,避免null检查
Command noCommand = new NoCommand();
for (int i = 0; i < 2; 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 NoCommand implements Command {
public void execute() {}
public void undo() {}
}
// 客户端代码
public class HomeAutomationDemo {
public static void main(String[] args) {
RemoteControl remote = new RemoteControl();
Light livingRoomLight = new Light("Living Room");
Stereo stereo = new Stereo();
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
StereoVolumeUpCommand stereoVolumeUp = new StereoVolumeUpCommand(stereo);
StereoVolumeUpCommand stereoVolumeDown = new StereoVolumeUpCommand(stereo);
remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remote.setCommand(1, stereoVolumeUp, stereoVolumeDown);
System.out.println("--- Testing Light ---");
remote.onButtonWasPushed(0);
remote.offButtonWasPushed(0);
remote.undoButtonWasPushed();
System.out.println("\n--- Testing Stereo ---");
remote.onButtonWasPushed(1);
remote.onButtonWasPushed(1);
remote.undoButtonWasPushed();
}
}
3.2 代码解析与关键点
在这个示例中,我们实现了以下功能:
- 创建了灯光和音响两个设备(接收者)
- 为每个设备创建了相应的命令对象
- 使用遥控器(调用者)来执行这些命令
- 实现了撤销功能
关键设计点:
- 使用接口统一命令的调用方式
- 命令对象封装了接收者和要执行的操作
- 调用者不需要知道命令的具体实现
- 通过保存最后执行的命令实现撤销功能
- 使用空对象模式处理未初始化的命令槽
注意:在实际项目中,命令对象通常是无状态的,它们只是调用接收者的方法。如果需要状态,应该将状态保存在接收者中,而不是命令对象中。
4. 命令模式的进阶应用
4.1 宏命令与命令队列
命令模式的一个强大特性是支持宏命令(Macro Command),即一个命令可以包含多个子命令:
java复制public class MacroCommand implements Command {
private Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
public void execute() {
for (Command command : commands) {
command.execute();
}
}
public void undo() {
// 需要按相反顺序撤销
for (int i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
}
}
// 使用示例
Command[] partyOn = {livingRoomLightOn, stereoVolumeUp};
Command[] partyOff = {livingRoomLightOff, stereoVolumeDown};
MacroCommand partyOnMacro = new MacroCommand(partyOn);
MacroCommand partyOffMacro = new MacroCommand(partyOff);
remote.setCommand(2, partyOnMacro, partyOffMacro);
System.out.println("\n--- Testing Macro Command ---");
remote.onButtonWasPushed(2);
remote.offButtonWasPushed(2);
4.2 命令队列与日志
命令模式还可以用于实现命令队列和命令日志:
java复制// 命令队列
public class CommandQueue {
private Queue<Command> queue = new LinkedList<>();
public void addCommand(Command command) {
queue.add(command);
}
public void executeAll() {
while (!queue.isEmpty()) {
Command command = queue.poll();
command.execute();
}
}
}
// 命令日志(用于实现撤销/重做)
public class CommandHistory {
private Stack<Command> history = new Stack<>();
public void push(Command command) {
history.push(command);
}
public Command pop() {
if (!history.isEmpty()) {
return history.pop();
}
return null;
}
}
// 修改后的RemoteControl支持完整历史记录
public class AdvancedRemoteControl {
private CommandHistory history = new CommandHistory();
public void executeCommand(Command command) {
command.execute();
history.push(command);
}
public void undoLastCommand() {
Command command = history.pop();
if (command != null) {
command.undo();
}
}
}
4.3 延迟执行与线程池
命令模式天然支持延迟执行和异步执行,因为命令对象可以在任何时间被执行:
java复制// 使用线程池执行命令
public class CommandExecutor {
private ExecutorService executor = Executors.newFixedThreadPool(5);
public void executeAsync(Command command) {
executor.submit(() -> command.execute());
}
public void shutdown() {
executor.shutdown();
}
}
// 使用示例
CommandExecutor executor = new CommandExecutor();
executor.executeAsync(livingRoomLightOn);
executor.executeAsync(stereoVolumeUp);
5. 命令模式在真实项目中的应用
5.1 GUI应用程序中的命令模式
在图形用户界面中,命令模式被广泛应用。例如:
- 菜单项和工具栏按钮通常绑定到命令对象
- 文本编辑器的撤销/重做功能
- 对话框的"确定"和"取消"操作
java复制// 简单的文本编辑器示例
public class TextEditor {
private String text = "";
private CommandHistory history = new CommandHistory();
public void executeCommand(Command command) {
command.execute();
history.push(command);
}
public void undo() {
Command command = history.pop();
if (command != null) {
command.undo();
}
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
public class AddTextCommand implements Command {
private TextEditor editor;
private String textToAdd;
public AddTextCommand(TextEditor editor, String textToAdd) {
this.editor = editor;
this.textToAdd = textToAdd;
}
public void execute() {
editor.setText(editor.getText() + textToAdd);
}
public void undo() {
String currentText = editor.getText();
editor.setText(currentText.substring(0, currentText.length() - textToAdd.length()));
}
}
// 使用示例
TextEditor editor = new TextEditor();
editor.executeCommand(new AddTextCommand(editor, "Hello "));
editor.executeCommand(new AddTextCommand(editor, "World!"));
System.out.println(editor.getText()); // 输出: Hello World!
editor.undo();
System.out.println(editor.getText()); // 输出: Hello
5.2 游戏开发中的命令模式
在游戏开发中,命令模式常用于:
- 处理用户输入(键盘、鼠标、手柄)
- 实现游戏回放系统
- AI行为控制
- 撤销/重做系统
java复制// 简单的游戏角色控制示例
public class GameCharacter {
private int x = 0;
private int y = 0;
public void moveUp() {
y++;
System.out.println("Character moved up to (" + x + "," + y + ")");
}
public void moveDown() {
y--;
System.out.println("Character moved down to (" + x + "," + y + ")");
}
public void moveLeft() {
x--;
System.out.println("Character moved left to (" + x + "," + y + ")");
}
public void moveRight() {
x++;
System.out.println("Character moved right to (" + x + "," + y + ")");
}
}
public class MoveCommand implements Command {
public enum Direction { UP, DOWN, LEFT, RIGHT }
private GameCharacter character;
private Direction direction;
public MoveCommand(GameCharacter character, Direction direction) {
this.character = character;
this.direction = direction;
}
public void execute() {
switch (direction) {
case UP: character.moveUp(); break;
case DOWN: character.moveDown(); break;
case LEFT: character.moveLeft(); break;
case RIGHT: character.moveRight(); break;
}
}
public void undo() {
switch (direction) {
case UP: character.moveDown(); break;
case DOWN: character.moveUp(); break;
case LEFT: character.moveRight(); break;
case RIGHT: character.moveLeft(); break;
}
}
}
// 使用示例
GameCharacter character = new GameCharacter();
CommandHistory gameHistory = new CommandHistory();
gameHistory.push(new MoveCommand(character, MoveCommand.Direction.RIGHT));
gameHistory.push(new MoveCommand(character, MoveCommand.Direction.UP));
gameHistory.push(new MoveCommand(character, MoveCommand.Direction.RIGHT));
// 执行所有命令
while (true) {
Command cmd = gameHistory.pop();
if (cmd == null) break;
cmd.execute();
}
// 现在可以按相反顺序撤销所有移动
5.3 事务系统中的命令模式
在需要事务支持的系统中,命令模式可以用来实现原子操作:
java复制public interface TransactionalCommand extends Command {
void commit();
void rollback();
}
public class TransferCommand implements TransactionalCommand {
private Account fromAccount;
private Account toAccount;
private int amount;
private boolean executed = false;
public TransferCommand(Account fromAccount, Account toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
public void execute() {
if (fromAccount.getBalance() >= amount) {
fromAccount.withdraw(amount);
toAccount.deposit(amount);
executed = true;
}
}
public void undo() {
if (executed) {
toAccount.withdraw(amount);
fromAccount.deposit(amount);
executed = false;
}
}
public void commit() {
// 在实际系统中,这里可能将事务标记为已提交
System.out.println("Transfer committed");
}
public void rollback() {
undo();
System.out.println("Transfer rolled back");
}
}
// 使用示例
Account accountA = new Account("A", 1000);
Account accountB = new Account("B", 500);
TransactionalCommand transfer = new TransferCommand(accountA, accountB, 200);
try {
transfer.execute();
// 其他操作...
transfer.commit();
} catch (Exception e) {
transfer.rollback();
}
6. 命令模式的优缺点与替代方案
6.1 命令模式的主要优点
- 解耦调用者与接收者:调用者不需要知道接收者的具体实现,只需要知道命令接口。
- 支持撤销/重做:通过维护命令历史,可以轻松实现撤销和重做功能。
- 支持延迟执行:命令对象可以在创建后的任何时间执行,支持排队和延迟执行。
- 支持宏命令:可以轻松组合多个命令形成复合命令。
- 易于扩展:新的命令可以很容易地添加到系统中,而不需要修改现有代码。
6.2 命令模式的潜在缺点
- 可能增加类的数量:每个具体命令都需要一个单独的类,可能导致类数量膨胀。
- 可能增加系统复杂性:对于简单操作,使用命令模式可能会显得过于复杂。
- 性能开销:命令对象的创建和执行可能带来额外的性能开销。
6.3 何时考虑使用命令模式
- 需要将操作请求与执行操作的对象解耦时
- 需要支持命令的撤销、重做或事务功能时
- 需要将命令排队、记录命令历史或支持延迟执行时
- 需要实现回调机制时
6.4 相关模式与替代方案
- 策略模式:策略模式关注的是算法的替换,而命令模式关注的是请求的封装。
- 备忘录模式:可以配合命令模式实现更强大的撤销功能。
- 责任链模式:可以将多个命令组织成责任链。
- 函数式接口:在支持函数式编程的语言中,可以使用函数对象替代命令对象。
在Java 8+中,可以使用lambda表达式简化命令模式的实现:
java复制// 使用lambda表达式创建命令
Command lightOn = () -> light.on();
Command lightOff = () -> light.off();
// 更简洁的命令调用
remote.setCommand(0, light::on, light::off);
7. 命令模式的最佳实践与常见问题
7.1 命令模式实现的最佳实践
- 保持命令对象轻量级:命令对象通常应该是无状态的,只包含执行操作所需的最小信息。
- 考虑使用不可变命令:一旦创建,命令对象不应该被修改,这有助于实现线程安全。
- 合理设计命令接口:根据需求决定是否需要支持撤销、重做等功能。
- 使用空对象模式:为未初始化的命令槽提供空命令对象,避免null检查。
- 考虑性能影响:对于高频创建的命令对象,可以考虑对象池技术。
7.2 常见问题与解决方案
问题1:如何处理大量相似命令?
解决方案:可以使用参数化命令,将变化的参数传递给命令对象,而不是为每个微小变化创建新的命令类。
java复制public class ParameterizedCommand implements Command {
private Receiver receiver;
private String parameter;
public ParameterizedCommand(Receiver receiver, String parameter) {
this.receiver = receiver;
this.parameter = parameter;
}
public void execute() {
receiver.action(parameter);
}
}
问题2:如何实现可组合的命令?
解决方案:使用组合模式创建复合命令,如前文所示的宏命令。
问题3:如何保证命令的线程安全?
解决方案:
- 使命令对象无状态
- 如果必须有状态,确保状态是不可变的
- 使用线程安全的集合来维护命令历史
问题4:如何处理命令执行失败的情况?
解决方案:在命令接口中添加状态检查方法,或使用异常处理机制。
java复制public interface SafeCommand extends Command {
boolean canExecute();
boolean isSuccessful();
}
public class SafeTransferCommand implements SafeCommand {
// ... 其他代码 ...
public boolean canExecute() {
return fromAccount.getBalance() >= amount;
}
public boolean isSuccessful() {
return executed;
}
}
7.3 性能优化建议
- 命令对象池:对于频繁创建和销毁的命令对象,可以使用对象池来重用实例。
- 轻量级命令:尽量保持命令对象轻量,将复杂状态存储在接收者中。
- 异步执行:对于耗时命令,考虑异步执行以避免阻塞调用线程。
- 批量处理:对于大量小命令,可以考虑批量处理以减少开销。
java复制// 命令对象池示例
public class CommandPool {
private Map<Class<?>, Queue<Command>> pool = new HashMap<>();
public <T extends Command> T acquire(Class<T> clazz) {
Queue<Command> queue = pool.computeIfAbsent(clazz, k -> new LinkedList<>());
Command cmd = queue.poll();
if (cmd == null) {
try {
cmd = clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return (T) cmd;
}
public void release(Command command) {
Queue<Command> queue = pool.computeIfAbsent(command.getClass(), k -> new LinkedList<>());
queue.offer(command);
}
}
8. 命令模式在不同语言中的实现差异
8.1 Java中的命令模式
Java中通常使用接口或抽象类定义命令,如前文示例所示。Java 8引入的lambda表达式和函数式接口可以简化命令模式的实现:
java复制@FunctionalInterface
public interface Command {
void execute();
}
// 使用lambda表达式创建命令
Command cmd = () -> System.out.println("Hello, Command Pattern!");
cmd.execute();
8.2 C++中的命令模式
C++中可以使用函数对象或std::function来实现命令模式:
cpp复制#include <iostream>
#include <functional>
#include <vector>
// 命令接口
using Command = std::function<void()>;
// 接收者
class Light {
public:
void on() { std::cout << "Light is on\n"; }
void off() { std::cout << "Light is off\n"; }
};
int main() {
Light light;
// 创建命令
Command lightOn = [&light]() { light.on(); };
Command lightOff = [&light]() { light.off(); };
// 执行命令
lightOn();
lightOff();
// 命令队列
std::vector<Command> commands = {lightOn, lightOff, lightOn};
for (auto& cmd : commands) {
cmd();
}
return 0;
}
8.3 Python中的命令模式
Python中可以使用简单的函数或类来实现命令模式:
python复制from abc import ABC, abstractmethod
# 命令接口
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# 具体命令
class LightOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.on()
def undo(self):
self.light.off()
# 接收者
class Light:
def on(self):
print("Light is on")
def off(self):
print("Light is off")
# 调用者
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
if self.command:
self.command.execute()
# 使用示例
light = Light()
light_on = LightOnCommand(light)
remote = RemoteControl()
remote.set_command(light_on)
remote.press_button()
8.4 JavaScript中的命令模式
JavaScript中可以使用函数或对象来实现命令模式:
javascript复制// 接收者
const light = {
on() { console.log('Light is on'); },
off() { console.log('Light is off'); }
};
// 命令工厂
function createCommand(receiver, action) {
return {
execute() { receiver[action](); },
undo() {
const opposite = { on: 'off', off: 'on' };
receiver[opposite[action]]();
}
};
}
// 调用者
class RemoteControl {
constructor() {
this.command = null;
this.history = [];
}
setCommand(command) {
this.command = command;
}
pressButton() {
if (this.command) {
this.command.execute();
this.history.push(this.command);
}
}
undo() {
const command = this.history.pop();
if (command) command.undo();
}
}
// 使用示例
const lightOn = createCommand(light, 'on');
const remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();
remote.undo();
9. 命令模式在现代框架中的应用
9.1 Spring框架中的命令模式
Spring框架中的JdbcTemplate使用了类似命令模式的回调机制:
java复制public class EmployeeDao {
private JdbcTemplate jdbcTemplate;
public List<Employee> findAll() {
return jdbcTemplate.query(
"SELECT * FROM employees",
(rs, rowNum) -> new Employee(
rs.getInt("id"),
rs.getString("name"),
rs.getString("department")
)
);
}
}
这里的RowMapper接口类似于命令接口,匿名内部类实现了具体的映射逻辑。
9.2 JavaFX中的命令模式
JavaFX的事件处理机制也使用了命令模式的思想:
java复制Button button = new Button("Click me");
button.setOnAction(event -> {
System.out.println("Button clicked!");
});
这里的EventHandler接口就是命令接口,lambda表达式实现了具体的命令逻辑。
9.3 Android中的命令模式
Android中的Intent可以看作是一种命令模式的实现:
java复制Intent intent = new Intent(this, OtherActivity.class);
intent.putExtra("key", "value");
startActivity(intent);
这里的Intent封装了启动Activity的请求,startActivity()方法执行这个"命令"。
10. 命令模式的测试与调试技巧
10.1 单元测试命令模式
测试命令模式时,应关注:
- 命令执行后接收者的状态是否正确改变
- 撤销操作是否能正确恢复状态
- 命令队列和历史记录是否正常工作
java复制public class LightOnCommandTest {
private Light light;
private LightOnCommand command;
@Before
public void setUp() {
light = new Light();
command = new LightOnCommand(light);
}
@Test
public void testExecute() {
command.execute();
assertTrue(light.isOn());
}
@Test
public void testUndo() {
command.execute();
command.undo();
assertFalse(light.isOn());
}
}
10.2 调试命令模式
调试命令模式时的一些技巧:
- 在命令的execute()和undo()方法中添加日志
- 检查命令历史记录是否正确维护
- 验证接收者状态是否符合预期
- 对于异步命令,检查线程安全和执行顺序
java复制public class LoggingCommand implements Command {
private Command delegate;
public LoggingCommand(Command delegate) {
this.delegate = delegate;
}
public void execute() {
System.out.println("Executing command: " + delegate.getClass().getSimpleName());
delegate.execute();
}
public void undo() {
System.out.println("Undoing command: " + delegate.getClass().getSimpleName());
delegate.undo();
}
}
// 使用装饰器模式添加日志
Command cmd = new LoggingCommand(new LightOnCommand(light));
cmd.execute();
10.3 性能测试建议
对于性能敏感的应用,应测试:
- 命令对象的创建开销
- 命令执行的时间成本
- 命令历史记录的内存占用
- 多线程环境下的吞吐量
可以使用JMH等微基准测试工具进行测试:
java复制@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class CommandBenchmark {
private Light light = new Light();
private Command command = new LightOnCommand(light);
@Benchmark
public void measureExecute() {
command.execute();
}
@Benchmark
public void measureExecuteAndUndo() {
command.execute();
command.undo();
}
}
11. 命令模式与其他模式的协同使用
11.1 命令模式与备忘录模式
备忘录模式可以增强命令模式的撤销功能,特别是在需要保存复杂状态时:
java复制// 备忘录
public class LightStateMemento {
private final boolean isOn;
public LightStateMemento(boolean isOn) {
this.isOn = isOn;
}
public boolean isOn() {
return isOn;
}
}
// 支持备忘录的命令
public class ToggleLightCommand implements Command {
private Light light;
private LightStateMemento previousState;
public ToggleLightCommand(Light light) {
this.light = light;
}
public void execute() {
previousState = new LightStateMemento(light.isOn());
light.toggle();
}
public void undo() {
if (previousState != null) {
if (previousState.isOn()) {
light.on();
} else {
light.off();
}
}
}
}
11.2 命令模式与责任链模式
可以将多个命令组织成责任链,按顺序执行:
java复制public class CommandChain implements Command {
private List<Command> commands = new ArrayList<>();
public void addCommand(Command command) {
commands.add(command);
}
public void execute() {
for (Command command : commands) {
command.execute();
}
}
public void undo() {
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
11.3 命令模式与组合模式
使用组合模式可以创建复杂的命令结构:
java复制// 组件接口
public interface CommandComponent {
void execute();
void undo();
}
// 叶子节点 - 基本命令
public class LightCommand implements CommandComponent {
private Light light;
private boolean turnOn;
public LightCommand(Light light, boolean turnOn) {
this.light = light;
this.turnOn = turnOn;
}
public void execute() {
if (turnOn) {
light.on();
} else {
light.off();
}
}
public void undo() {
if (turnOn) {
light.off();
} else {
light.on();
}
}
}
// 复合节点 - 命令组
public class CommandGroup implements CommandComponent {
private List<CommandComponent> children = new ArrayList<>();
public void add(CommandComponent component) {
children.add(component);
}
public void execute() {
for (CommandComponent component : children) {
component.execute();
}
}
public void undo() {
for (int i = children.size() - 1; i >= 0; i--) {
children.get(i).undo();
}
}
}
12. 命令模式的变体与扩展
12.1 持久化命令
将命令序列化并保存到磁盘或数据库,可以实现持久化的事务日志:
java复制public interface PersistentCommand extends Command, Serializable {
// 可以添加持久化相关的方法
}
public class PersistentLightOnCommand implements PersistentCommand {
private Light light;
public PersistentLightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
// 保存命令到文件
public void saveCommand(Command command, String filename) throws IOException {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {
out.writeObject(command);
}
}
// 从文件加载命令
public Command loadCommand(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
return (Command) in.readObject();
}
}
12.2 异步命令
将命令执行移到后台线程:
java复制public class AsyncCommand implements Command {
private Command delegate;
private Executor executor;
public AsyncCommand(Command delegate, Executor executor) {
this.delegate = delegate;
this.executor = executor;
}
public void execute() {
executor.execute(() -> delegate.execute());
}
public void undo() {
// 异步命令的撤销通常比较复杂
// 可能需要额外的机制来跟踪执行状态
}
}
12.3 事务命令
实现原子性操作序列:
java复制public class TransactionCommand implements Command {
private List<Command> commands = new ArrayList<>();
public void addCommand(Command command) {
commands.add(command);
}
public void execute() {
try {
for (Command command : commands) {
command.execute();
}
} catch (Exception e) {
undo();
throw new RuntimeException("Transaction failed", e);
}
}
public void undo() {
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
13. 命令模式在微服务架构中的应用
13.1 命令查询职责分离(CQRS)
CQRS模式中,命令和查询被明确分离,命令部分可以看作是命令模式的应用:
java复制// 命令接口
public interface UserCommand {
void execute(UserRepository repository);
}
// 具体命令
public class CreateUserCommand implements UserCommand {
private String username;
private String email;
public CreateUserCommand(String username, String email) {
this.username = username;
this.email = email;
}
public void execute(UserRepository repository) {
User user = new User(username, email);
repository.save(user);
}
}
// 命令处理器
public class CommandDispatcher {
private UserRepository repository;
public CommandDispatcher(UserRepository repository) {
this.repository = repository;
}
public void dispatch(UserCommand command) {
command.execute(repository);
}
}
13.2 事件溯源(Event Sourcing)
事件溯源可以看作是命令模式的扩展,每个事件相当于一个命令:
java复制public interface Event {
void apply(AggregateRoot aggregate);
}
public class UserCreatedEvent implements Event {
private String userId;
private String username;
public UserCreatedEvent(String userId, String username) {
this.userId = userId;
this.username = username;
}
public void apply(AggregateRoot aggregate) {
User user = (User) aggregate;
user.setId(userId);
user.setUsername(username);
}
}
public class EventStore {
private List<Event> events = new ArrayList<>();
public void addEvent(Event event) {
events.add(event);
}
public void replay(AggregateRoot aggregate) {
for (Event event : events) {
event.apply(aggregate);
}
}
}
14. 命令模式的反模式与误用
14.1 常见反模式
- 过度使用命令模式:对于简单操作,直接调用方法可能更合适。
- 命令类膨胀:为每个微小变化创建新命令类,导致类数量
