15. 命令模式(Command)
15. 命令模式(Command)
问题:想把「操作」封装成对象,支持撤销、队列、日志等。
核心:每个操作是一个命令对象,包含执行和撤销方法。
interface Command {
execute(): void;
unexecute(): void;
}
class Light {
#state: boolean = false;
on(): void { this.#state = true; console.log('灯开了'); }
off(): void { this.#state = false; console.log('灯关了'); }
}
class LightOnCommand implements Command {
#light: Light;
constructor(light: Light) { this.#light = light; }
execute(): void { this.#light.on(); }
unexecute(): void { this.#light.off(); }
}
class RemoteControl {
#history: Command[] = [];
execute(command: Command): void {
command.execute();
this.#history.push(command);
}
undo(): void {
this.#history.pop()?.unexecute();
}
}
const remote = new RemoteControl();
const light = new Light();
remote.execute(new LightOnCommand(light)); // 灯开了
remote.undo(); // 灯关了package command
import "fmt"
type Command interface {
Execute()
Unexecute()
}
type Light struct{ state bool }
func (l *Light) On() { l.state = true; fmt.Println("灯开了") }
func (l *Light) Off() { l.state = false; fmt.Println("灯关了") }
type LightOnCommand struct{ light *Light }
func (c LightOnCommand) Execute() { c.light.On() }
func (c LightOnCommand) Unexecute() { c.light.Off() }
type RemoteControl struct {
history []Command
}
func (r *RemoteControl) Execute(cmd Command) {
cmd.Execute()
r.history = append(r.history, cmd)
}
func (r *RemoteControl) Undo() {
if len(r.history) == 0 {
return
}
cmd := r.history[len(r.history)-1]
r.history = r.history[:len(r.history)-1]
cmd.Unexecute()
}实战场景
- 撤销和重做:编辑器里的输入、删除、移动、格式化都封装成命令,执行后入栈,撤销时反向执行。
- 操作队列:批量导入、发送邮件、生成报表等耗时任务可以排成命令队列异步执行。
- 宏命令:把一组操作组合成一个命令,例如“格式化文档”内部包含缩进、排序、修复空行。
- 快捷键绑定:键盘快捷键只绑定命令对象,不直接绑定具体业务函数,便于重映射。
- 菜单和工具栏:菜单项、按钮、右键操作都触发同一个命令,避免多个入口复制逻辑。
- 任务调度:定时任务、延迟任务、重试任务都可以保存为命令并由 scheduler 执行。
- 审计日志:命令天然记录了“谁在什么时候请求了什么操作”,适合关键业务留痕。
- 远程执行:客户端把命令序列化发到服务端执行,例如协作编辑、远程控制、自动化脚本。
- 事务脚本:一组命令要么全部成功,要么失败后补偿,常用于订单、库存、支付流程。
- 测试自动化:UI 测试或端到端脚本可以表示成命令列表,便于复用和回放。