设计模式手册013:命令模式 - 请求封装的优雅之道
本文是「设计模式手册」系列第013篇,我们将深入探讨命令模式,这种模式将请求封装为对象,从而让你可以用不同的请求、队列或日志来参数化其他对象,同时支持可撤销的操作。
1. 场景:我们为何需要命令模式?
在软件开发中,我们经常遇到需要将请求发送者与接收者解耦的场景:
- 图形界面操作:按钮点击触发复杂业务逻辑
- 任务队列:将操作封装为任务,支持延迟执行和撤销
- 事务系统:支持操作的事务性和回滚
- 宏命令:将多个操作组合成一个原子操作
- 远程调用:将本地调用封装为可网络传输的命令对象
传统做法的困境:
java
// 紧耦合的设计 - 按钮直接调用业务逻辑
public class Button {
private String label;
public Button(String label) {
this.label = label;
}
// 问题:按钮直接依赖具体的业务逻辑
public void onClick() {
if ("保存".equals(label)) {
new Document().save();
} else if ("打开".equals(label)) {
new Document().open();
} else if ("打印".equals(label)) {
new Document().print();
}
// 每增加一个功能,就要修改Button类
}
}
public class Document {
public void save() {
System.out.println("保存文档...");
}
public void open() {
System.out.println("打开文档...");
}
public void print() {
System.out.println("打印文档...");
}
}
// 菜单类也有同样的问题
public class Menu {
public void selectMenuItem(String itemName) {
if ("保存".equals(itemName)) {
new Document().save();
} else if ("打开".equals(itemName)) {
new Document().open();
}
// 重复的判断逻辑
}
}
// 使用示例
public class TightCouplingExample {
public static void main(String[] args) {
Button saveButton = new Button("保存");
Button openButton = new Button("打开");
saveButton.onClick(); // 输出:保存文档...
openButton.onClick(); // 输出:打开文档...
}
}
这种实现的痛点:
- 紧耦合:UI组件直接依赖业务逻辑
- 违反开闭原则:新增功能需要修改现有代码
- 代码重复:相同的判断逻辑在不同UI组件中重复
- 难以扩展:无法支持撤销、重做、宏命令等高级功能
2. 命令模式:定义与本质
2.1 模式定义
命令模式(Command Pattern):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
2.2 核心角色
java
// 命令接口:声明执行操作的方法
public interface Command {
void execute();
void undo(); // 支持撤销操作
}
// 具体命令:实现命令接口,将一个接收者对象绑定于一个动作
public class SaveCommand implements Command {
private Document document;
private String previousContent; // 用于撤销操作
public SaveCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
// 保存当前状态以便撤销
this.previousContent = document.getContent();
document.save();
}
@Override
public void undo() {
if (previousContent != null) {
document.setContent(previousContent);
System.out.println("撤销保存操作,恢复内容为: " + previousContent);
}
}
}
public class OpenCommand implements Command {
private Document document;
public OpenCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
document.open();
}
@Override
public void undo() {
System.out.println("无法撤销打开操作");
}
}
public class PrintCommand implements Command {
private Document document;
public PrintCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
document.print();
}
@Override
public void undo() {
System.out.println("无法撤销打印操作");
}
}
// 接收者:知道如何实施与执行一个请求相关的操作
public class Document {
private String content;
private String filename;
public Document() {
this.content = "";
this.filename = "未命名文档";
}
public void save() {
System.out.println("保存文档: " + filename);
// 实际的保存逻辑
}
public void open() {
System.out.println("打开文档: " + filename);
// 实际的打开逻辑
}
public void print() {
System.out.println("打印文档: " + filename);
System.out.println("内容: " + content);
// 实际的打印逻辑
}
// getter和setter
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String getFilename() { return filename; }
public void setFilename(String filename) { this.filename = filename; }
}
// 调用者:要求该命令执行这个请求
public class Button {
private String label;
private Command command;
public Button(String label, Command command) {
this.label = label;
this.command = command;
}
public void onClick() {
System.out.print("按钮[" + label + "]被点击 - ");
command.execute();
}
// 支持动态改变命令
public void setCommand(Command command) {
this.command = command;
}
}
// 使用命令模式
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建接收者
Document document = new Document();
document.setContent("这是一个重要的文档内容");
document.setFilename("报告.docx");
// 创建具体命令
Command saveCommand = new SaveCommand(document);
Command openCommand = new OpenCommand(document);
Command printCommand = new PrintCommand(document);
// 创建调用者并绑定命令
Button saveButton = new Button("保存", saveCommand);
Button openButton = new Button("打开", openCommand);
Button printButton = new Button("打印", printCommand);
// 执行命令
saveButton.onClick(); // 输出:按钮[保存]被点击 - 保存文档: 报告.docx
openButton.onClick(); // 输出:按钮[打开]被点击 - 打开文档: 报告.docx
printButton.onClick(); // 输出:按钮[打印]被点击 - 打印文档: 报告.docx
// 测试撤销
saveCommand.undo(); // 输出:撤销保存操作,恢复内容为: 这是一个重要的文档内容
}
}
3. 深入理解:命令模式的多维视角
3.1 第一重:简单命令 vs 复杂命令
简单命令
java
// 简单命令:直接执行单一操作
public class SimpleCommand implements Command {
private Runnable action;
public SimpleCommand(Runnable action) {
this.action = action;
}
@Override
public void execute() {
action.run();
}
@Override
public void undo() {
System.out.println("简单命令不支持撤销");
}
}
// 使用函数式接口创建命令
public class FunctionalCommand implements Command {
private final Runnable executeAction;
private final Runnable undoAction;
public FunctionalCommand(Runnable executeAction, Runnable undoAction) {
this.executeAction = executeAction;
this.undoAction = undoAction;
}
@Override
public void execute() {
executeAction.run();
}
@Override
public void undo() {
if (undoAction != null) {
undoAction.run();
}
}
}
复杂命令(宏命令)
java
// 宏命令:包含多个命令的复合命令
public class MacroCommand implements Command {
private List<Command> commands = new ArrayList<>();
private List<Command> executedCommands = new ArrayList<>();
public void addCommand(Command command) {
commands.add(command);
}
public void removeCommand(Command command) {
commands.remove(command);
}
@Override
public void execute() {
executedCommands.clear();
for (Command command : commands) {
command.execute();
executedCommands.add(command);
}
}
@Override
public void undo() {
// 按相反顺序撤销所有已执行的命令
for (int i = executedCommands.size() - 1; i >= 0; i--) {
executedCommands.get(i).undo();
}
executedCommands.clear();
}
}
3.2 第二重:命令的历史管理
java
// 命令历史管理器
public class CommandHistory {
private Stack<Command> history = new Stack<>();
private Stack<Command> redoStack = new Stack<>();
public void execute(Command command) {
command.execute();
history.push(command);
redoStack.clear(); // 执行新命令时清空重做栈
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo();
redoStack.push(command);
} else {
System.out.println("没有可以撤销的操作");
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command command = redoStack.pop();
command.execute();
history.push(command);
} else {
System.out.println("没有可以重做的操作");
}
}
public void clear() {
history.clear();
redoStack.clear();
}
public int getHistorySize() {
return history.size();
}
public int getRedoSize() {
return redoStack.size();
}
}
3.3 第三重:参数化命令
java
// 带参数的命令
public class ParameterizedCommand implements Command {
private Document document;
private String newContent;
private String previousContent;
public ParameterizedCommand(Document document, String newContent) {
this.document = document;
this.newContent = newContent;
}
@Override
public void execute() {
this.previousContent = document.getContent();
document.setContent(newContent);
System.out.println("文档内容已更新为: " + newContent);
}
@Override
public void undo() {
document.setContent(previousContent);
System.out.println("撤销内容修改,恢复为: " + previousContent);
}
}
// 支持动态参数的命令
public class DynamicCommand implements Command {
private Consumer<Object[]> executeFunction;
private Consumer<Object[]> undoFunction;
private Object[] executeParams;
private Object[] undoParams;
public DynamicCommand(Consumer<Object[]> executeFunction,
Consumer<Object[]> undoFunction,
Object[] executeParams,
Object[] undoParams) {
this.executeFunction = executeFunction;
this.undoFunction = undoFunction;
this.executeParams = executeParams;
this.undoParams = undoParams;
}
@Override
public void execute() {
executeFunction.accept(executeParams);
}
@Override
public void undo() {
undoFunction.accept(undoParams);
}
}
4. 实战案例:完整的文本编辑器
java
// 文本编辑器 - 接收者
public class TextEditor {
private String content = "";
private int cursorPosition = 0;
private String clipboard = "";
public void insertText(String text) {
String before = content.substring(0, cursorPosition);
String after = content.substring(cursorPosition);
content = before + text + after;
cursorPosition += text.length();
System.out.println("插入文本: \"" + text + "\",当前内容: \"" + content + "\"");
}
public void deleteText(int length) {
if (cursorPosition >= length) {
String deleted = content.substring(cursorPosition - length, cursorPosition);
content = content.substring(0, cursorPosition - length) +
content.substring(cursorPosition);
cursorPosition -= length;
System.out.println("删除文本: \"" + deleted + "\",当前内容: \"" + content + "\"");
}
}
public void copyText(int start, int end) {
if (start >= 0 && end <= content.length() && start < end) {
clipboard = content.substring(start, end);
System.out.println("复制文本: \"" + clipboard + "\"");
}
}
public void pasteText() {
if (!clipboard.isEmpty()) {
insertText(clipboard);
}
}
public void setCursorPosition(int position) {
if (position >= 0 && position <= content.length()) {
cursorPosition = position;
System.out.println("设置光标位置: " + position);
}
}
public void boldText(int start, int end) {
if (start >= 0 && end <= content.length() && start < end) {
String selected = content.substring(start, end);
content = content.substring(0, start) +
"**" + selected + "**" +
content.substring(end);
System.out.println("加粗文本: \"" + selected + "\",当前内容: \"" + content + "\"");
}
}
// getter方法
public String getContent() { return content; }
public int getCursorPosition() { return cursorPosition; }
public String getClipboard() { return clipboard; }
// 用于命令模式的状态保存
public EditorState saveState() {
return new EditorState(content, cursorPosition, clipboard);
}
public void restoreState(EditorState state) {
this.content = state.getContent();
this.cursorPosition = state.getCursorPosition();
this.clipboard = state.getClipboard();
System.out.println("恢复编辑器状态");
}
}
// 编辑器状态(用于撤销操作)
@Data
public class EditorState {
private final String content;
private final int cursorPosition;
private final String clipboard;
public EditorState(String content, int cursorPosition, String clipboard) {
this.content = content;
this.cursorPosition = cursorPosition;
this.clipboard = clipboard;
}
}
// 文本编辑命令
public class InsertCommand implements Command {
private TextEditor editor;
private String text;
private EditorState previousState;
public InsertCommand(TextEditor editor, String text) {
this.editor = editor;
this.text = text;
}
@Override
public void execute() {
this.previousState = editor.saveState();
editor.insertText(text);
}
@Override
public void undo() {
editor.restoreState(previousState);
}
}
public class DeleteCommand implements Command {
private TextEditor editor;
private int length;
private EditorState previousState;
public DeleteCommand(TextEditor editor, int length) {
this.editor = editor;
this.length = length;
}
@Override
public void execute() {
this.previousState = editor.saveState();
editor.deleteText(length);
}
@Override
public void undo() {
editor.restoreState(previousState);
}
}
public class CopyCommand implements Command {
private TextEditor editor;
private int start;
private int end;
public CopyCommand(TextEditor editor, int start, int end) {
this.editor = editor;
this.start = start;
this.end = end;
}
@Override
public void execute() {
editor.copyText(start, end);
}
@Override
public void undo() {
System.out.println("无法撤销复制操作");
}
}
public class PasteCommand implements Command {
private TextEditor editor;
private EditorState previousState;
public PasteCommand(TextEditor editor) {
this.editor = editor;
}
@Override
public void execute() {
this.previousState = editor.saveState();
editor.pasteText();
}
@Override
public void undo() {
editor.restoreState(previousState);
}
}
public class BoldCommand implements Command {
private TextEditor editor;
private int start;
private int end;
private EditorState previousState;
public BoldCommand(TextEditor editor, int start, int end) {
this.editor = editor;
this.start = start;
this.end = end;
}
@Override
public void execute() {
this.previousState = editor.saveState();
editor.boldText(start, end);
}
@Override
public void undo() {
editor.restoreState(previousState);
}
}
// 编辑器应用 - 调用者
public class TextEditorApp {
private TextEditor editor = new TextEditor();
private CommandHistory history = new CommandHistory();
// UI组件
public void clickInsertButton(String text) {
Command command = new InsertCommand(editor, text);
history.execute(command);
}
public void clickDeleteButton(int length) {
Command command = new DeleteCommand(editor, length);
history.execute(command);
}
public void clickCopyButton(int start, int end) {
Command command = new CopyCommand(editor, start, end);
history.execute(command);
}
public void clickPasteButton() {
Command command = new PasteCommand(editor);
history.execute(command);
}
public void clickBoldButton(int start, int end) {
Command command = new BoldCommand(editor, start, end);
history.execute(command);
}
public void undo() {
history.undo();
}
public void redo() {
history.redo();
}
public void printStatus() {
System.out.println("=== 编辑器状态 ===");
System.out.println("内容: " + editor.getContent());
System.out.println("光标位置: " + editor.getCursorPosition());
System.out.println("剪贴板: " + editor.getClipboard());
System.out.println("历史记录: " + history.getHistorySize() + " 个操作");
System.out.println("可重做: " + history.getRedoSize() + " 个操作");
System.out.println();
}
}
// 使用示例
public class TextEditorDemo {
public static void main(String[] args) {
TextEditorApp app = new TextEditorApp();
System.out.println("=== 文本编辑器演示 ===\n");
// 执行一系列操作
app.clickInsertButton("Hello, ");
app.printStatus();
app.clickInsertButton("World!");
app.printStatus();
app.clickCopyButton(0, 5); // 复制 "Hello"
app.printStatus();
app.clickPasteButton(); // 粘贴 "Hello"
app.printStatus();
app.clickBoldButton(6, 11); // 加粗 "World"
app.printStatus();
// 测试撤销和重做
System.out.println("=== 测试撤销和重做 ===\n");
app.undo(); // 撤销加粗
app.printStatus();
app.undo(); // 撤销粘贴
app.printStatus();
app.redo(); // 重做粘贴
app.printStatus();
app.undo(); // 再次撤销粘贴
app.printStatus();
// 宏命令演示
System.out.println("=== 宏命令演示 ===\n");
MacroCommand macro = new MacroCommand();
macro.addCommand(new InsertCommand(app.editor, "这是"));
macro.addCommand(new InsertCommand(app.editor, "一个"));
macro.addCommand(new InsertCommand(app.editor, "宏命令"));
app.history.execute(macro);
app.printStatus();
app.undo(); // 撤销整个宏命令
app.printStatus();
}
}
5. Spring框架中的命令模式
5.1 Spring的事务管理
java
// 模拟Spring的事务命令
public interface TransactionalCommand extends Command {
void setTransactionManager(TransactionManager transactionManager);
}
// 事务命令基类
public abstract class AbstractTransactionalCommand implements TransactionalCommand {
protected TransactionManager transactionManager;
protected TransactionStatus transactionStatus;
@Override
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void execute() {
transactionStatus = transactionManager.getTransaction(null);
try {
doInTransaction();
transactionManager.commit(transactionStatus);
} catch (Exception e) {
transactionManager.rollback(transactionStatus);
throw new RuntimeException("事务执行失败", e);
}
}
@Override
public void undo() {
// 事务命令的撤销就是回滚
if (transactionStatus != null && !transactionStatus.isCompleted()) {
transactionManager.rollback(transactionStatus);
}
}
protected abstract void doInTransaction();
}
// 具体的事务命令
@Component
public class UserRegistrationCommand extends AbstractTransactionalCommand {
private final UserRepository userRepository;
private final EmailService emailService;
private User user;
@Autowired
public UserRegistrationCommand(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public void setUser(User user) {
this.user = user;
}
@Override
protected void doInTransaction() {
// 在事务中执行用户注册逻辑
userRepository.save(user);
emailService.sendWelcomeEmail(user.getEmail());
System.out.println("用户注册成功: " + user.getUsername());
}
}
// 事务管理器
@Component
public class TransactionManager {
public TransactionStatus getTransaction(Object definition) {
System.out.println("开始事务");
return new DefaultTransactionStatus();
}
public void commit(TransactionStatus status) {
if (status.isCompleted()) {
throw new IllegalStateException("事务已完成");
}
status.setCompleted(true);
System.out.println("提交事务");
}
public void rollback(TransactionStatus status) {
if (status.isCompleted()) {
throw new IllegalStateException("事务已完成");
}
status.setCompleted(true);
System.out.println("回滚事务");
}
}
// 事务状态
public class DefaultTransactionStatus implements TransactionStatus {
private boolean completed = false;
@Override
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}
// 服务层使用事务命令
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final TransactionManager transactionManager;
@Autowired
public UserService(UserRepository userRepository, EmailService emailService,
TransactionManager transactionManager) {
this.userRepository = userRepository;
this.emailService = emailService;
this.transactionManager = transactionManager;
}
public void registerUser(User user) {
UserRegistrationCommand command = new UserRegistrationCommand(userRepository, emailService);
command.setUser(user);
command.setTransactionManager(transactionManager);
command.execute();
}
}
5.2 Spring的JdbcTemplate
java
// 模拟Spring JdbcTemplate的命令模式应用
@Component
public class JdbcTemplate {
public <T> T execute(ConnectionCallback<T> action) {
return execute(action, true);
}
public <T> T execute(ConnectionCallback<T> action, boolean autoClose) {
Connection connection = null;
try {
connection = getConnection();
return action.doInConnection(connection);
} catch (SQLException e) {
throw new RuntimeException("数据库操作失败", e);
} finally {
if (autoClose && connection != null) {
closeConnection(connection);
}
}
}
public <T> T query(String sql, ResultSetExtractor<T> extractor, Object... args) {
return execute(connection -> {
try (PreparedStatement ps = connection.prepareStatement(sql)) {
setParameters(ps, args);
try (ResultSet rs = ps.executeQuery()) {
return extractor.extractData(rs);
}
}
});
}
public int update(String sql, Object... args) {
return execute(connection -> {
try (PreparedStatement ps = connection.prepareStatement(sql)) {
setParameters(ps, args);
return ps.executeUpdate();
}
});
}
// 命令接口
public interface ConnectionCallback<T> {
T doInConnection(Connection connection) throws SQLException;
}
public interface ResultSetExtractor<T> {
T extractData(ResultSet rs) throws SQLException;
}
// 具体命令
public class QueryCommand<T> implements ConnectionCallback<T> {
private final String sql;
private final ResultSetExtractor<T> extractor;
private final Object[] args;
public QueryCommand(String sql, ResultSetExtractor<T> extractor, Object... args) {
this.sql = sql;
this.extractor = extractor;
this.args = args;
}
@Override
public T doInConnection(Connection connection) throws SQLException {
try (PreparedStatement ps = connection.prepareStatement(sql)) {
setParameters(ps, args);
try (ResultSet rs = ps.executeQuery()) {
return extractor.extractData(rs);
}
}
}
}
private Connection getConnection() {
// 获取数据库连接
System.out.println("获取数据库连接");
return null; // 实际返回Connection
}
private void closeConnection(Connection connection) {
// 关闭数据库连接
System.out.println("关闭数据库连接");
}
private void setParameters(PreparedStatement ps, Object[] args) throws SQLException {
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
}
}
6. 命令模式的进阶用法
6.1 异步命令执行
java
// 异步命令执行器
@Component
public class AsyncCommandExecutor {
private final ExecutorService executorService;
private final CommandHistory history;
public AsyncCommandExecutor() {
this.executorService = Executors.newFixedThreadPool(10);
this.history = new CommandHistory();
}
public CompletableFuture<Void> executeAsync(Command command) {
return CompletableFuture.runAsync(() -> {
System.out.println("异步执行命令: " + command.getClass().getSimpleName());
history.execute(command);
}, executorService);
}
public CompletableFuture<Void> executeAsync(List<Command> commands) {
List<CompletableFuture<Void>> futures = commands.stream()
.map(this::executeAsync)
.collect(Collectors.toList());
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
public void shutdown() {
executorService.shutdown();
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
// 支持回调的异步执行
public CompletableFuture<Void> executeWithCallback(Command command, Runnable onSuccess, Runnable onError) {
return CompletableFuture.runAsync(() -> {
try {
history.execute(command);
if (onSuccess != null) {
onSuccess.run();
}
} catch (Exception e) {
if (onError != null) {
onError.run();
}
}
}, executorService);
}
}
// 可取消的命令
public abstract class CancellableCommand implements Command {
private volatile boolean cancelled = false;
private volatile Thread executionThread;
@Override
public void execute() {
executionThread = Thread.currentThread();
try {
doExecute();
} finally {
executionThread = null;
}
}
public void cancel() {
cancelled = true;
if (executionThread != null) {
executionThread.interrupt();
}
}
protected boolean isCancelled() {
return cancelled || Thread.currentThread().isInterrupted();
}
protected abstract void doExecute();
}
// 长时间运行的任务命令
public class LongRunningTaskCommand extends CancellableCommand {
private final String taskName;
private final int durationSeconds;
public LongRunningTaskCommand(String taskName, int durationSeconds) {
this.taskName = taskName;
this.durationSeconds = durationSeconds;
}
@Override
protected void doExecute() {
System.out.println("开始执行长时间任务: " + taskName);
for (int i = 1; i <= durationSeconds; i++) {
if (isCancelled()) {
System.out.println("任务被取消: " + taskName);
return;
}
try {
Thread.sleep(1000);
System.out.println(taskName + " - 进度: " + i + "/" + durationSeconds + " 秒");
} catch (InterruptedException e) {
System.out.println("任务被中断: " + taskName);
Thread.currentThread().interrupt();
return;
}
}
System.out.println("任务完成: " + taskName);
}
@Override
public void undo() {
System.out.println("无法撤销长时间运行的任务: " + taskName);
}
}
6.2 命令的序列化和持久化
java
// 可序列化的命令
public abstract class SerializableCommand implements Command, Serializable {
private static final long serialVersionUID = 1L;
protected String commandId;
protected Date createdAt;
public SerializableCommand() {
this.commandId = UUID.randomUUID().toString();
this.createdAt = new Date();
}
public String getCommandId() {
return commandId;
}
public Date getCreatedAt() {
return createdAt;
}
}
// 命令存储服务
@Component
public class CommandStore {
private final String storagePath;
public CommandStore() {
this.storagePath = "commands/";
createStorageDirectory();
}
public void saveCommand(SerializableCommand command) {
String filename = storagePath + command.getCommandId() + ".cmd";
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(command);
System.out.println("命令已保存: " + filename);
} catch (IOException e) {
throw new RuntimeException("保存命令失败", e);
}
}
public SerializableCommand loadCommand(String commandId) {
String filename = storagePath + commandId + ".cmd";
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
return (SerializableCommand) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("加载命令失败", e);
}
}
public List<SerializableCommand> loadAllCommands() {
File dir = new File(storagePath);
File[] files = dir.listFiles((d, name) -> name.endsWith(".cmd"));
if (files == null) {
return Collections.emptyList();
}
return Arrays.stream(files)
.map(file -> loadCommand(file.getName().replace(".cmd", "")))
.sorted(Comparator.comparing(SerializableCommand::getCreatedAt))
.collect(Collectors.toList());
}
private void createStorageDirectory() {
File dir = new File(storagePath);
if (!dir.exists()) {
dir.mkdirs();
}
}
}
// 持久化文本命令
public class PersistentInsertCommand extends SerializableCommand {
private String text;
private transient TextEditor editor; // 不序列化
public PersistentInsertCommand(TextEditor editor, String text) {
super();
this.editor = editor;
this.text = text;
}
@Override
public void execute() {
if (editor != null) {
editor.insertText(text);
} else {
System.out.println("执行持久化命令: 插入文本 \"" + text + "\"");
}
}
@Override
public void undo() {
System.out.println("撤销持久化命令: " + commandId);
}
// 用于反序列化后重新注入依赖
public void setEditor(TextEditor editor) {
this.editor = editor;
}
}
7. 命令模式 vs 其他模式
7.1 命令模式 vs 策略模式
- 命令模式:封装操作和参数,支持撤销、队列、日志等功能
- 策略模式:封装算法,在运行时选择不同的算法实现
7.2 命令模式 vs 备忘录模式
- 命令模式:封装操作请求,支持操作的执行和撤销
- 备忘录模式:保存和恢复对象状态,用于实现撤销功能
7.3 命令模式 vs 责任链模式
- 命令模式:将请求封装为对象,发送给接收者处理
- 责任链模式:多个处理器按顺序尝试处理请求
8. 总结与思考
8.1 命令模式的优点
- 解耦调用者和接收者:调用者不需要知道具体的接收者
- 支持撤销和重做:可以轻松实现操作的撤销和重做功能
- 支持操作队列:可以将命令放入队列,延迟执行或批量执行
- 支持宏命令:可以将多个命令组合成一个复合命令
- 易于扩展:新增命令不需要修改现有代码
8.2 命令模式的缺点
- 类数量增加:每个命令都需要一个具体的命令类
- 复杂性增加:系统需要管理命令的历史、队列等
- 性能开销:命令的创建和管理可能带来性能开销
- 过度设计:简单场景下可能过度设计
8.3 设计思考
命令模式的本质是**"请求的对象化"**。它通过将请求封装为对象,使得请求可以被参数化、队列化、日志化和撤销化,从而提供了极大的灵活性。
深入思考的角度:
"命令模式的核心价值在于它将'做什么'和'谁来做'、'什么时候做'分离开来。这种分离使得我们可以构建高度灵活和可扩展的系统,支持复杂的操作管理需求。"
在实际应用中,命令模式有很多优秀的实践:
- GUI应用程序的菜单和按钮系统
- 事务处理系统的操作管理
- 任务调度系统的任务封装
- 游戏开发中的输入处理
- 远程过程调用(RPC)的请求封装
从系统设计的角度看,命令模式特别适合以下场景:
- 需要将操作请求参数化
- 需要支持操作的撤销和重做
- 需要将操作放入队列或在不同的时间执行
- 需要支持事务操作
- 需要记录操作日志
最佳实践建议:
- 仔细设计命令接口,考虑撤销、重做等需求
- 使用命令历史管理器来管理命令的执行历史
- 考虑使用宏命令来组合多个相关操作
- 对于耗时操作,考虑实现异步命令执行
- 在分布式系统中,考虑命令的序列化和持久化
使用场景判断:
- 适合:需要撤销/重做、操作队列、事务支持、宏命令的场景
- 不适合:简单的一次性操作、性能要求极高的场景
下一篇预告:设计模式手册014 - 解释器模式:如何定义语言的文法,并用解释器来解释语言中的句子?
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。