命令模式详解
目录
命令模式简介
定义
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而让你可以用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销的操作。
核心思想
- 封装请求:将请求封装成对象,使请求参数化
- 解耦调用者和接收者:调用者不需要知道具体的接收者
- 支持撤销和重做:可以记录命令历史,实现撤销功能
- 支持宏命令:可以将多个命令组合成一个复合命令
模式结构
- Command(命令接口):声明执行操作的接口
- ConcreteCommand(具体命令):实现命令接口,绑定接收者
- Invoker(调用者):调用命令对象执行请求
- Receiver(接收者):知道如何执行与请求相关的操作
- Client(客户端):创建具体命令对象并设置接收者
核心流程
命令模式流程图
基本实现流程
1. 定义命令接口
public interface Command {
void execute();
void undo();
}
2. 定义接收者
public class Light {
private boolean isOn = false;
public void turnOn() {
isOn = true;
System.out.println("灯已打开");
}
public void turnOff() {
isOn = false;
System.out.println("灯已关闭");
}
public boolean isOn() {
return isOn;
}
}
3. 实现具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn();
}
}
4. 定义调用者
public class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
undoCommand = new NoCommand();
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
if (onCommands[slot] != null) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
}
public void offButtonWasPushed(int slot) {
if (offCommands[slot] != null) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
}
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
5. 客户端使用
public class Client {
public static void main(String[] args) {
// 创建接收者
Light light = new Light();
// 创建命令
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
// 创建调用者
RemoteControl remote = new RemoteControl();
remote.setCommand(0, lightOn, lightOff);
// 执行命令
remote.onButtonWasPushed(0);
remote.offButtonWasPushed(0);
remote.undoButtonWasPushed();
}
}
重难点分析
重难点1:命令的撤销和重做
问题描述
实现命令的撤销和重做功能需要考虑状态管理和历史记录。
解决方案
public class CommandHistory {
private Stack<Command> history = new Stack<>();
private Stack<Command> redoHistory = new Stack<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
redoHistory.clear(); // 清空重做历史
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
redoHistory.push(command);
}
}
public void redo() {
if (!redoHistory.isEmpty()) {
Command command = redoHistory.pop();
command.execute();
history.push(command);
}
}
}
// 支持状态保存的命令
public class LightOnCommand implements Command {
private Light light;
private boolean previousState;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
previousState = light.isOn();
light.turnOn();
}
@Override
public void undo() {
if (previousState) {
light.turnOn();
} else {
light.turnOff();
}
}
}
重难点2:宏命令的实现
问题描述
如何将多个命令组合成一个复合命令。
解决方案
public class MacroCommand implements Command {
private List<Command> commands;
public MacroCommand(List<Command> commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
}
}
@Override
public void undo() {
// 逆序执行撤销
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
// 使用宏命令
public class PartyMode {
public static void main(String[] args) {
Light light = new Light();
TV tv = new TV();
Stereo stereo = new Stereo();
List<Command> partyCommands = Arrays.asList(
new LightOnCommand(light),
new TVOnCommand(tv),
new StereoOnCommand(stereo)
);
Command partyMode = new MacroCommand(partyCommands);
RemoteControl remote = new RemoteControl();
remote.setCommand(0, partyMode, new NoCommand());
remote.onButtonWasPushed(0); // 执行所有命令
}
}
重难点3:命令队列和异步执行
问题描述
如何实现命令的队列管理和异步执行。
解决方案
public class CommandQueue {
private Queue<Command> queue = new LinkedList<>();
private ExecutorService executor = Executors.newFixedThreadPool(5);
private volatile boolean running = true;
public void addCommand(Command command) {
synchronized (queue) {
queue.offer(command);
queue.notify();
}
}
public void start() {
executor.submit(() -> {
while (running) {
Command command = null;
synchronized (queue) {
while (queue.isEmpty() && running) {
try {
queue.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
if (running) {
command = queue.poll();
}
}
if (command != null) {
try {
command.execute();
} catch (Exception e) {
System.err.println("命令执行失败: " + e.getMessage());
}
}
}
});
}
public void stop() {
running = false;
synchronized (queue) {
queue.notifyAll();
}
executor.shutdown();
}
}
重难点4:命令的持久化和恢复
问题描述
如何将命令持久化到文件或数据库,并在系统重启后恢复。
解决方案
public interface SerializableCommand extends Command, Serializable {
String getCommandType();
Map<String, Object> getParameters();
}
public class LightOnCommand implements SerializableCommand {
private String lightId;
private transient Light light; // 不序列化
public LightOnCommand(String lightId) {
this.lightId = lightId;
}
@Override
public void execute() {
if (light == null) {
light = LightManager.getInstance().getLight(lightId);
}
light.turnOn();
}
@Override
public String getCommandType() {
return "LIGHT_ON";
}
@Override
public Map<String, Object> getParameters() {
Map<String, Object> params = new HashMap<>();
params.put("lightId", lightId);
return params;
}
}
public class CommandPersistence {
private static final String COMMAND_FILE = "commands.ser";
public void saveCommands(List<SerializableCommand> commands) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(COMMAND_FILE))) {
oos.writeObject(commands);
} catch (IOException e) {
System.err.println("保存命令失败: " + e.getMessage());
}
}
@SuppressWarnings("unchecked")
public List<SerializableCommand> loadCommands() {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(COMMAND_FILE))) {
return (List<SerializableCommand>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("加载命令失败: " + e.getMessage());
return new ArrayList<>();
}
}
}
Spring中的源码分析
CommandLineRunner接口
@FunctionalInterface
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
// 使用示例
@Component
public class DatabaseInitializer implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("初始化数据库...");
// 初始化逻辑
}
}
ApplicationRunner接口
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
// 使用示例
@Component
public class ConfigLoader implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("加载配置...");
// 配置加载逻辑
}
}
Spring Boot中的命令模式应用
// 自定义命令接口
public interface TaskCommand {
void execute();
void undo();
String getDescription();
}
// 具体命令实现
@Component
public class DatabaseBackupCommand implements TaskCommand {
@Autowired
private DatabaseService databaseService;
@Override
public void execute() {
System.out.println("开始数据库备份...");
databaseService.backup();
System.out.println("数据库备份完成");
}
@Override
public void undo() {
System.out.println("撤销数据库备份...");
// 撤销逻辑
}
@Override
public String getDescription() {
return "数据库备份";
}
}
// 命令调度器
@Component
public class TaskScheduler {
private final Map<String, TaskCommand> commands = new HashMap<>();
@Autowired
public TaskScheduler(List<TaskCommand> commandList) {
for (TaskCommand command : commandList) {
commands.put(command.getDescription(), command);
}
}
public void executeCommand(String commandName) {
TaskCommand command = commands.get(commandName);
if (command != null) {
command.execute();
} else {
System.out.println("命令不存在: " + commandName);
}
}
public void listCommands() {
System.out.println("可用命令:");
commands.keySet().forEach(System.out::println);
}
}
Spring MVC中的命令模式
// 命令对象
public class UserRegistrationCommand {
private String username;
private String email;
private String password;
// getter和setter方法
public void validate() {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
}
}
// 命令处理器
@Service
public class UserRegistrationHandler {
@Autowired
private UserService userService;
@Autowired
private EmailService emailService;
public void handle(UserRegistrationCommand command) {
// 验证命令
command.validate();
// 执行注册
User user = userService.register(command.getUsername(),
command.getEmail(),
command.getPassword());
// 发送确认邮件
emailService.sendConfirmationEmail(user.getEmail());
}
}
// 控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserRegistrationHandler registrationHandler;
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody UserRegistrationCommand command) {
try {
registrationHandler.handle(command);
return ResponseEntity.ok("注册成功");
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
具体使用场景
1. 智能家居控制系统
// 设备接口
public interface Device {
void on();
void off();
boolean isOn();
}
// 具体设备
public class Light implements Device {
private boolean isOn = false;
@Override
public void on() {
isOn = true;
System.out.println("灯已打开");
}
@Override
public void off() {
isOn = false;
System.out.println("灯已关闭");
}
@Override
public boolean isOn() {
return isOn;
}
}
// 命令接口
public interface Command {
void execute();
void undo();
}
// 具体命令
public class DeviceOnCommand implements Command {
private Device device;
public DeviceOnCommand(Device device) {
this.device = device;
}
@Override
public void execute() {
device.on();
}
@Override
public void undo() {
device.off();
}
}
// 智能遥控器
public class SmartRemote {
private Map<String, Command> commands = new HashMap<>();
private Stack<Command> history = new Stack<>();
public void setCommand(String button, Command command) {
commands.put(button, command);
}
public void pressButton(String button) {
Command command = commands.get(button);
if (command != null) {
command.execute();
history.push(command);
}
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
}
}
}
2. 文本编辑器
// 文档类
public class Document {
private StringBuilder content = new StringBuilder();
public void insert(int position, String text) {
content.insert(position, text);
}
public void delete(int start, int end) {
content.delete(start, end);
}
public String getContent() {
return content.toString();
}
}
// 命令接口
public interface TextCommand {
void execute();
void undo();
}
// 插入命令
public class InsertCommand implements TextCommand {
private Document document;
private int position;
private String text;
public InsertCommand(Document document, int position, String text) {
this.document = document;
this.position = position;
this.text = text;
}
@Override
public void execute() {
document.insert(position, text);
}
@Override
public void undo() {
document.delete(position, position + text.length());
}
}
// 删除命令
public class DeleteCommand implements TextCommand {
private Document document;
private int start;
private int end;
private String deletedText;
public DeleteCommand(Document document, int start, int end) {
this.document = document;
this.start = start;
this.end = end;
this.deletedText = document.getContent().substring(start, end);
}
@Override
public void execute() {
document.delete(start, end);
}
@Override
public void undo() {
document.insert(start, deletedText);
}
}
// 编辑器
public class TextEditor {
private Document document = new Document();
private Stack<TextCommand> history = new Stack<>();
private Stack<TextCommand> redoHistory = new Stack<>();
public void insert(int position, String text) {
TextCommand command = new InsertCommand(document, position, text);
command.execute();
history.push(command);
redoHistory.clear();
}
public void delete(int start, int end) {
TextCommand command = new DeleteCommand(document, start, end);
command.execute();
history.push(command);
redoHistory.clear();
}
public void undo() {
if (!history.isEmpty()) {
TextCommand command = history.pop();
command.undo();
redoHistory.push(command);
}
}
public void redo() {
if (!redoHistory.isEmpty()) {
TextCommand command = redoHistory.pop();
command.execute();
history.push(command);
}
}
public String getContent() {
return document.getContent();
}
}
3. 任务调度系统
// 任务命令接口
public interface TaskCommand {
void execute();
void cancel();
String getTaskId();
TaskStatus getStatus();
}
// 任务状态
public enum TaskStatus {
PENDING, RUNNING, COMPLETED, FAILED, CANCELLED
}
// 具体任务命令
public class DataProcessingTask implements TaskCommand {
private String taskId;
private TaskStatus status = TaskStatus.PENDING;
private String dataSource;
private String outputPath;
public DataProcessingTask(String taskId, String dataSource, String outputPath) {
this.taskId = taskId;
this.dataSource = dataSource;
this.outputPath = outputPath;
}
@Override
public void execute() {
status = TaskStatus.RUNNING;
try {
System.out.println("开始处理数据: " + dataSource);
// 模拟数据处理
Thread.sleep(5000);
System.out.println("数据处理完成,输出到: " + outputPath);
status = TaskStatus.COMPLETED;
} catch (InterruptedException e) {
status = TaskStatus.CANCELLED;
Thread.currentThread().interrupt();
} catch (Exception e) {
status = TaskStatus.FAILED;
System.err.println("任务执行失败: " + e.getMessage());
}
}
@Override
public void cancel() {
status = TaskStatus.CANCELLED;
System.out.println("任务已取消: " + taskId);
}
@Override
public String getTaskId() {
return taskId;
}
@Override
public TaskStatus getStatus() {
return status;
}
}
// 任务调度器
public class TaskScheduler {
private Map<String, TaskCommand> tasks = new HashMap<>();
private ExecutorService executor = Executors.newFixedThreadPool(10);
public void submitTask(TaskCommand task) {
tasks.put(task.getTaskId(), task);
executor.submit(() -> task.execute());
}
public void cancelTask(String taskId) {
TaskCommand task = tasks.get(taskId);
if (task != null) {
task.cancel();
}
}
public TaskStatus getTaskStatus(String taskId) {
TaskCommand task = tasks.get(taskId);
return task != null ? task.getStatus() : null;
}
public void shutdown() {
executor.shutdown();
}
}
4. 数据库事务管理
// 数据库操作命令
public interface DatabaseCommand {
void execute() throws SQLException;
void rollback() throws SQLException;
String getDescription();
}
// 插入命令
public class InsertCommand implements DatabaseCommand {
private Connection connection;
private String sql;
private Object[] parameters;
public InsertCommand(Connection connection, String sql, Object... parameters) {
this.connection = connection;
this.sql = sql;
this.parameters = parameters;
}
@Override
public void execute() throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
for (int i = 0; i < parameters.length; i++) {
stmt.setObject(i + 1, parameters[i]);
}
stmt.executeUpdate();
}
}
@Override
public void rollback() throws SQLException {
// 插入操作的回滚需要删除记录
// 这里简化处理
System.out.println("回滚插入操作");
}
@Override
public String getDescription() {
return "INSERT: " + sql;
}
}
// 更新命令
public class UpdateCommand implements DatabaseCommand {
private Connection connection;
private String sql;
private Object[] parameters;
private Object[] originalValues;
public UpdateCommand(Connection connection, String sql, Object[] parameters, Object[] originalValues) {
this.connection = connection;
this.sql = sql;
this.parameters = parameters;
this.originalValues = originalValues;
}
@Override
public void execute() throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
for (int i = 0; i < parameters.length; i++) {
stmt.setObject(i + 1, parameters[i]);
}
stmt.executeUpdate();
}
}
@Override
public void rollback() throws SQLException {
// 更新操作的回滚需要恢复原值
String rollbackSql = sql.replace("SET", "SET").replace("WHERE", "WHERE");
try (PreparedStatement stmt = connection.prepareStatement(rollbackSql)) {
for (int i = 0; i < originalValues.length; i++) {
stmt.setObject(i + 1, originalValues[i]);
}
stmt.executeUpdate();
}
}
@Override
public String getDescription() {
return "UPDATE: " + sql;
}
}
// 事务管理器
public class TransactionManager {
private List<DatabaseCommand> commands = new ArrayList<>();
private Connection connection;
public TransactionManager(Connection connection) {
this.connection = connection;
}
public void addCommand(DatabaseCommand command) {
commands.add(command);
}
public void execute() throws SQLException {
try {
connection.setAutoCommit(false);
for (DatabaseCommand command : commands) {
command.execute();
}
connection.commit();
System.out.println("事务提交成功");
} catch (SQLException e) {
connection.rollback();
System.out.println("事务回滚");
throw e;
} finally {
connection.setAutoCommit(true);
}
}
public void rollback() throws SQLException {
try {
connection.setAutoCommit(false);
// 逆序回滚
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).rollback();
}
connection.commit();
System.out.println("事务回滚完成");
} catch (SQLException e) {
System.err.println("回滚失败: " + e.getMessage());
throw e;
} finally {
connection.setAutoCommit(true);
}
}
}
面试高频点
面试知识点思维导图
1. 命令模式的基本概念
问题:什么是命令模式?
答案要点:
- 将请求封装成对象,使请求参数化
- 解耦调用者和接收者
- 支持撤销、重做、宏命令等功能
- 属于行为型设计模式
问题:命令模式有哪些角色?
答案要点:
- Command(命令接口):声明执行操作的接口
- ConcreteCommand(具体命令):实现命令接口,绑定接收者
- Invoker(调用者):调用命令对象执行请求
- Receiver(接收者):知道如何执行与请求相关的操作
- Client(客户端):创建具体命令对象并设置接收者
2. 实现方式相关
问题:如何实现命令模式?
答案要点:
// 1. 定义命令接口
public interface Command {
void execute();
void undo();
}
// 2. 定义接收者
public class Light {
public void turnOn() { System.out.println("灯已打开"); }
public void turnOff() { System.out.println("灯已关闭"); }
}
// 3. 实现具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
// 4. 定义调用者
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
3. 重难点问题
问题:如何实现命令的撤销和重做?
答案要点:
// 1. 保存命令历史
public class CommandHistory {
private Stack<Command> history = new Stack<>();
private Stack<Command> redoHistory = new Stack<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
redoHistory.clear();
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
redoHistory.push(command);
}
}
public void redo() {
if (!redoHistory.isEmpty()) {
Command command = redoHistory.pop();
command.execute();
history.push(command);
}
}
}
// 2. 保存状态信息
public class LightOnCommand implements Command {
private Light light;
private boolean previousState;
@Override
public void execute() {
previousState = light.isOn();
light.turnOn();
}
@Override
public void undo() {
if (previousState) {
light.turnOn();
} else {
light.turnOff();
}
}
}
问题:如何实现宏命令?
答案要点:
public class MacroCommand implements Command {
private List<Command> commands;
public MacroCommand(List<Command> commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
}
}
@Override
public void undo() {
// 逆序执行撤销
for (int i = commands.size() - 1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
4. Spring中的应用
问题:Spring中如何使用命令模式?
答案要点:
// 1. CommandLineRunner接口
@Component
public class DatabaseInitializer implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("初始化数据库...");
}
}
// 2. ApplicationRunner接口
@Component
public class ConfigLoader implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("加载配置...");
}
}
// 3. MVC中的命令对象
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<String> createUser(@RequestBody UserCreateCommand command) {
// 处理命令
return ResponseEntity.ok("用户创建成功");
}
}
5. 设计原则相关
问题:命令模式体现了哪些设计原则?
答案要点:
- 开闭原则:可以添加新命令而不修改现有代码
- 单一职责:每个命令只负责一个操作
- 依赖倒置:依赖抽象而不是具体实现
- 里氏替换:具体命令可以替换命令接口
6. 实际应用场景
问题:命令模式适用于哪些场景?
答案要点:
- 智能家居:遥控器控制各种设备
- 文本编辑器:撤销、重做功能
- 任务调度:异步任务执行
- 数据库事务:事务的提交和回滚
- GUI操作:菜单、按钮操作
- 日志记录:操作日志和审计
7. 与其他模式的对比
问题:命令模式与策略模式的区别?
答案要点:
- 目的:命令模式封装请求,策略模式封装算法
- 调用时机:命令模式可以延迟调用,策略模式立即调用
- 撤销支持:命令模式支持撤销,策略模式不支持
- 复杂度:命令模式更复杂,策略模式更简单
问题:命令模式与模板方法模式的区别?
答案要点:
- 结构:命令模式是对象行为,模板方法是类行为
- 继承:命令模式使用组合,模板方法使用继承
- 灵活性:命令模式更灵活,模板方法相对固定
- 使用场景:命令模式用于请求封装,模板方法用于算法框架
总结
命令模式是一种强大的行为型设计模式,它通过将请求封装成对象,实现了调用者和接收者的解耦,并提供了撤销、重做、宏命令等强大功能。
核心优势
- 解耦:调用者和接收者松耦合
- 灵活性:支持撤销、重做、宏命令
- 扩展性:易于添加新命令
- 可维护性:命令可以独立测试和维护
注意事项
- 复杂度:增加了系统的复杂度
- 内存消耗:需要保存命令历史
- 性能考虑:大量命令时需要考虑性能
- 状态管理:撤销功能需要合理管理状态
在实际开发中,命令模式特别适用于需要支持撤销操作、宏命令、任务调度等场景。通过合理使用命令模式,可以大大提高系统的灵活性和可维护性。
2429

被折叠的 条评论
为什么被折叠?



