设计模式- 命令模式详解
命令模式详解
目录
- 命令模式简介
- 核心流程
- 重难点分析
- Spring中的源码分析
- 具体使用场景
- 面试高频点
命令模式简介
定义
命令模式(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;}@Overridepublic void execute() {light.turnOn();}@Overridepublic void undo() {light.turnOff();}
}public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}@Overridepublic 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;}@Overridepublic void execute() {previousState = light.isOn();light.turnOn();}@Overridepublic 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;}@Overridepublic void execute() {for (Command command : commands) {command.execute();}}@Overridepublic 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;}@Overridepublic void execute() {if (light == null) {light = LightManager.getInstance().getLight(lightId);}light.turnOn();}@Overridepublic String getCommandType() {return "LIGHT_ON";}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Autowiredprivate DatabaseService databaseService;@Overridepublic void execute() {System.out.println("开始数据库备份...");databaseService.backup();System.out.println("数据库备份完成");}@Overridepublic void undo() {System.out.println("撤销数据库备份...");// 撤销逻辑}@Overridepublic String getDescription() {return "数据库备份";}
}// 命令调度器
@Component
public class TaskScheduler {private final Map<String, TaskCommand> commands = new HashMap<>();@Autowiredpublic 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 {@Autowiredprivate UserService userService;@Autowiredprivate 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 {@Autowiredprivate 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;@Overridepublic void on() {isOn = true;System.out.println("灯已打开");}@Overridepublic void off() {isOn = false;System.out.println("灯已关闭");}@Overridepublic 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;}@Overridepublic void execute() {device.on();}@Overridepublic 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;}@Overridepublic void execute() {document.insert(position, text);}@Overridepublic 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);}@Overridepublic void execute() {document.delete(start, end);}@Overridepublic 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;}@Overridepublic 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());}}@Overridepublic void cancel() {status = TaskStatus.CANCELLED;System.out.println("任务已取消: " + taskId);}@Overridepublic String getTaskId() {return taskId;}@Overridepublic 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;}@Overridepublic 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();}}@Overridepublic void rollback() throws SQLException {// 插入操作的回滚需要删除记录// 这里简化处理System.out.println("回滚插入操作");}@Overridepublic 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;}@Overridepublic 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();}}@Overridepublic 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();}}@Overridepublic 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;}@Overridepublic void execute() {light.turnOn();}@Overridepublic 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;@Overridepublic void execute() {previousState = light.isOn();light.turnOn();}@Overridepublic 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;}@Overridepublic void execute() {for (Command command : commands) {command.execute();}}@Overridepublic 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 {@Overridepublic void run(String... args) throws Exception {System.out.println("初始化数据库...");}
}// 2. ApplicationRunner接口
@Component
public class ConfigLoader implements ApplicationRunner {@Overridepublic 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. 与其他模式的对比
问题:命令模式与策略模式的区别?
答案要点:
- 目的:命令模式封装请求,策略模式封装算法
- 调用时机:命令模式可以延迟调用,策略模式立即调用
- 撤销支持:命令模式支持撤销,策略模式不支持
- 复杂度:命令模式更复杂,策略模式更简单
问题:命令模式与模板方法模式的区别?
答案要点:
- 结构:命令模式是对象行为,模板方法是类行为
- 继承:命令模式使用组合,模板方法使用继承
- 灵活性:命令模式更灵活,模板方法相对固定
- 使用场景:命令模式用于请求封装,模板方法用于算法框架
总结
命令模式是一种强大的行为型设计模式,它通过将请求封装成对象,实现了调用者和接收者的解耦,并提供了撤销、重做、宏命令等强大功能。
核心优势
- 解耦:调用者和接收者松耦合
- 灵活性:支持撤销、重做、宏命令
- 扩展性:易于添加新命令
- 可维护性:命令可以独立测试和维护
注意事项
- 复杂度:增加了系统的复杂度
- 内存消耗:需要保存命令历史
- 性能考虑:大量命令时需要考虑性能
- 状态管理:撤销功能需要合理管理状态
在实际开发中,命令模式特别适用于需要支持撤销操作、宏命令、任务调度等场景。通过合理使用命令模式,可以大大提高系统的灵活性和可维护性。