命令模式
行为型模式
访问次数: 10
命令模式将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
视频教程
类图
classDiagram
class Command {
<<interface>>
+execute()
+undo()
}
class ConcreteCommand {
-receiver: Receiver
+execute()
+undo()
}
class Receiver {
+action()
}
class Invoker {
-command: Command
+setCommand(command: Command)
+executeCommand()
}
class Client {
+createCommand()
}
Command <|-- ConcreteCommand
ConcreteCommand --> Receiver
Invoker --> Command
Client --> ConcreteCommand
Client --> Receiver
Client --> Invoker
源代码示例
// 命令接口
interface Command {
void execute();
void undo();
}
// 接收者
class Receiver {
public void action() {
System.out.println("Receiver action");
}
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
@Override
public void undo() {
System.out.println("Undo action");
}
}
// 调用者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
// 客户端
class Client {
public void createCommand() {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
# Python代码待添加
应用场景
当需要将请求封装成对象,支持撤销操作、日志记录、队列化请求时。应用场景包括:
1)文本编辑器中的撤销/重做功能,将每个编辑操作封装成命令对象
2)宏命令的录制和回放,将一系列操作封装成宏命令
3)图形编辑器中的操作历史,支持撤销和重做图形操作
4)数据库事务管理,将数据库操作封装成命令对象,支持事务回滚
5)游戏中的操作记录,记录玩家的操作序列,支持回放和撤销
6)批处理系统,将多个操作封装成批处理命令,支持批量执行和回滚
精选场景详解 —— 文本编辑器:撤销 / 重做操作历史
问题背景
编辑器支持输入、删除、粘贴。用户需要 Ctrl+Z / Ctrl+Y。若只改文档缓冲区而不记录操作,无法可靠回滚。
模式如何解决
选用命令模式:把每次编辑封装为 Command(execute/undo)。Invoker(编辑器 UI)执行命令并将其压入历史栈;撤销时 pop 并 undo,重做再 execute。
收益:
1)请求者与接收者(Document)解耦;
2)天然支持队列、日志、宏命令(一组命令组合);
3)可序列化命令做操作审计。
这是命令模式最经典的教学与实战场景之一。
场景模型(角色映射)
将模式中的抽象角色映射到该业务领域的具体类:
classDiagram
class Command {
<<interface>>
+execute()
+undo()
}
class InsertTextCommand {
-doc: Document
-pos: int
-text: String
+execute()
+undo()
}
class DeleteTextCommand {
-doc: Document
-pos: int
-len: int
+execute()
+undo()
}
class Document {
+insert(pos: int, text: String)
+delete(pos: int, len: int)
}
class EditorInvoker {
-history: Stack~Command~
-redo: Stack~Command~
+run(cmd: Command)
+undo()
+redo()
}
Command <|.. InsertTextCommand
Command <|.. DeleteTextCommand
InsertTextCommand --> Document
DeleteTextCommand --> Document
EditorInvoker --> Command