命令模式通过封装请求为对象实现撤销功能,Go中定义Command接口含Execute和Undo方法,以TextEditor为接收者处理插入删除操作,InsertCommand和DeleteCommand保存执行上下文并实现正向执行与反向撤销逻辑,CommandHistory维护命令栈管理执行与撤销顺序,客户端按序执行命令并可逐级撤销,支持多步回退且符合开闭原则,便于扩展重做与组合命令。

在Go语言中,命令模式(Command Pattern)是一种行为设计模式,它将请求封装为对象,从而使你可以用不同的请求对客户进行参数化。使用命令模式实现撤销功能,可以让程序支持操作的回退,比如文本编辑器中的“撤销键入”、“撤销删除”等场景。
命令模式核心结构
命令模式通常包含以下几个角色:
- Command(命令接口):定义执行和撤销操作的方法。
- ConcreteCommand(具体命令):实现命令接口,持有接收者对象,并在执行方法中调用接收者的相应操作。
- Receiver(接收者):真正执行操作的对象。
- Invoker(调用者):触发命令执行的对象,通常会维护一个命令历史栈用于撤销。
- Client(客户端):创建命令对象并绑定接收者。
定义命令接口与基础结构
首先定义一个命令接口,包含Execute()和Undo()两个方法:
type Command interface {
Execute()
Undo()
}
假设我们要实现一个简单的文本编辑器,支持插入文本和删除文本,并能撤销这些操作。
立即学习“go语言免费学习笔记(深入)”;
实现接收者:文本编辑器
接收者是实际处理数据的对象:
type TextEditor struct {
content string
}
func (t *TextEditor) Insert(text string) {
t.content += text
}
func (t *TextEditor) Delete(length int) {
if length >= len(t.content) {
t.content = ""
} else {
t.content = t.content[:len(t.content)-length]
}
}
func (t *TextEditor) GetContent() string {
return t.content
}
实现具体命令
每个操作都封装成一个命令对象,保存执行所需的数据,以便撤销时恢复状态。
插入命令:
type InsertCommand struct {
editor *TextEditor
text string
}
func NewInsertCommand(editor TextEditor, text string) InsertCommand {
return &InsertCommand{editor: editor, text: text}
}
func (c *InsertCommand) Execute() {
c.editor.Insert(c.text)
}
func (c *InsertCommand) Undo() {
// 撤销插入:删除最后插入的内容
c.editor.Delete(len(c.text))
}
删除命令:
type DeleteCommand struct {
editor *TextEditor
deleted string // 保存被删除的内容,用于撤销
}
func NewDeleteCommand(editor TextEditor, length int) DeleteCommand {
end := len(editor.content)
start := end - length
if start < 0 {
start = 0
}
deleted := editor.content[start:end]
return &DeleteCommand{
editor: editor,
deleted: deleted,
}
}
func (c *DeleteCommand) Execute() {
c.editor.Delete(len(c.deleted))
}
func (c *DeleteCommand) Undo() {
// 撤销删除:重新插入之前删掉的内容
c.editor.Insert(c.deleted)
}
调用者:支持撤销的命令管理器
命令调用者负责执行命令并维护历史记录:
type CommandHistory struct {
history []Command
index int // 当前位置,支持未来可能的重做
}
func NewCommandHistory() *CommandHistory {
return &CommandHistory{
history: make([]Command, 0),
index: -1,
}
}
func (ch *CommandHistory) Execute(command Command) {
// 执行新命令时,丢弃“未来”的命令(重做部分)
ch.history = ch.history[:ch.index+1]
ch.index++
ch.history = append(ch.history, command)
command.Execute()
}
func (ch *CommandHistory) Undo() {
if ch.index >= 0 {
command := ch.history[ch.index]
command.Undo()
ch.index--
}
}
客户端使用示例
现在将所有组件组合起来:
func main() {
editor := &TextEditor{}
history := NewCommandHistory()
// 插入 "Hello"
cmd1 := NewInsertCommand(editor, "Hello")
history.Execute(cmd1)
fmt.Println("After insert 'Hello':", editor.GetContent()) // Hello
// 插入 " World"
cmd2 := NewInsertCommand(editor, " World")
history.Execute(cmd2)
fmt.Println("After insert ' World':", editor.GetContent()) // Hello World
// 撤销一次
history.Undo()
fmt.Println("After undo:", editor.GetContent()) // Hello
// 撤销第二次
history.Undo()
fmt.Println("After second undo:", editor.GetContent()) // ""
// 删除部分内容再撤销
cmd3 := NewDeleteCommand(editor, 5)
history.Execute(cmd3)
fmt.Println("After delete 5 chars:", editor.GetContent()) // ""
history.Undo()
fmt.Println("After undo delete:", editor.GetContent()) // Hello}
总结
通过命令模式,Golang可以优雅地实现撤销功能。关键点在于:
- 每个操作封装为命令对象,包含正向执行和反向撤销逻辑。
- 命令对象保存足够的上下文信息(如删除内容),以便正确撤销。
- 使用历史栈管理命令顺序,支持多级撤销。
这种模式让新增操作变得容易,符合开闭原则,也提升了代码的可维护性。在实际项目中,还可扩展支持重做(Redo)、事务型命令、命令组合等功能。
基本上就这些。










