命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

使用背景

  1. 解耦请求发送者和请求接收者:当需要解耦请求发送者和请求接收者时,可以使用命令模式。命令模式将请求封装成一个独立的对象,从而使得请求发送者和接收者之间的关系松耦合,请求发送者不需要知道请求的具体接收者。

  2. 支持撤销和重做操作:命令模式可以很容易地支持撤销和重做操作。通过将请求封装成对象,可以在执行请求前将其记录下来,从而可以轻松地撤销或重做这些请求。

  3. 支持事务:命令模式可以支持事务操作。将一系列操作封装成一个命令对象,并且在执行时保证这些操作要么全部成功执行,要么全部不执行。

  4. 日志和回滚操作:命令模式可以用于记录操作日志,并且在需要时执行回滚操作。通过将操作封装成命令对象,并将这些命令对象保存到日志中,可以在需要时执行这些命令对象来回滚操作。

  5. 队列请求:命令模式可以用于实现队列请求。将请求封装成命令对象,并将这些命令对象存储在队列中,然后按照队列顺序执行这些命令对象。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package main

import "fmt"

// Command 接口定义了命令的执行和撤销操作
type Command interface {
Execute()
Undo()
}

// Editor 文本编辑器
type Editor struct {
content string
}

// AddTextCommand 添加文本命令
type AddTextCommand struct {
editor *Editor
text string
}

func (a *AddTextCommand) Execute() {
a.editor.content += a.text
}

func (a *AddTextCommand) Undo() {
a.editor.content = a.editor.content[:len(a.editor.content)-len(a.text)]
}

// UndoRedoHistory 撤销重做历史记录
type UndoRedoHistory struct {
commands []Command
}

func (h *UndoRedoHistory) ExecuteCommand(command Command) {
command.Execute()
h.commands = append(h.commands, command)
}

func (h *UndoRedoHistory) Undo() {
if len(h.commands) == 0 {
return
}
lastCommand := h.commands[len(h.commands)-1]
lastCommand.Undo()
h.commands = h.commands[:len(h.commands)-1]
}

func main() {
editor := &Editor{}
history := &UndoRedoHistory{}

// 添加文本命令
addTextCommand1 := &AddTextCommand{editor, "Hello, "}
history.ExecuteCommand(addTextCommand1)

// 添加第二个文本命令
addTextCommand2 := &AddTextCommand{editor, "world!"}
history.ExecuteCommand(addTextCommand2)

// 打印当前内容
fmt.Println("当前内容:", editor.content)

// 撤销上一个命令
history.Undo()
fmt.Println("撤销后内容:", editor.content)

// 重做上一个命令
history.ExecuteCommand(addTextCommand2)
fmt.Println("重做后内容:", editor.content)
}

结语

命令模式可以实现命令的执行和撤销操作,同时使得命令发送者和接收者之间的解耦。通过将命令封装成对象,我们可以很容易地实现撤销、重做、事务等功能,并且可以更加灵活地管理命令的执行顺序。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。