介绍
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而允许你使用不同的请求、队列或者日志请求来参数化其他对象。命令模式也支持可撤销的操作。
涉及的角色有:
- Command(命令):声明执行操作的接口。
- ConcreteCommand(具体命令):实现Command接口,负责执行具体的操作。
- Invoker(调用者):调用命令对象执行请求。
- Receiver(接收者):知道如何实施与执行一个请求相关的操作。
优点
- 解耦发送者和接收者:命令模式通过将请求封装成对象,使得发送者和接收者之间解耦。发送者不需要知道接收者的具体实现,只需要调用命令对象的执行方法。
- 可扩展性:由于命令模式将请求封装成对象,因此可以很容易地添加新的命令类,而无需修改已有的代码。这种可扩展性使得命令模式在需要添加新的请求或者修改现有请求的处理逻辑时非常灵活。
- 支持撤销和重做:命令模式可以在命令对象中保存请求的状态,从而支持撤销和重做操作。通过保存命令的历史记录,可以实现撤销一系列操作或者重新执行之前的操作。
缺点
- 类的数量增加:使用命令模式会增加类的数量,因为每个具体命令都需要实现一个命令类。当命令较多时,会导致类的数量增加,增加了系统的复杂性。
- 可能导致系统过于复杂:如果命令的操作逻辑较为复杂,那么命令模式可能会导致系统过于复杂。在这种情况下,可能需要额外的设计模式来管理和组织命令对象之间的关系,以确保系统的可维护性和可理解性。
使用场景
命令模式适用于需要将请求封装成对象,并支持撤销、重做、动态配置请求的场景。它可以提高系统的灵活性和可维护性,降低模块之间的耦合度。
-
用户界面操作:命令模式可以应用于用户界面操作,例如菜单、工具栏或按钮。每个菜单项、工具栏按钮或按钮都可以是一个命令对象,当用户执行操作时,调用相应的命令对象进行处理。这样可以将用户操作和具体的操作逻辑解耦,支持撤销、重做和动态配置操作。
-
队列请求处理:命令模式可以用于构建队列请求处理系统。请求被封装成命令对象,并添加到请求队列中。后台线程或者任务调度器从队列中获取命令对象并执行。这样可以实现请求的异步处理、延迟执行、日志记录等。
-
撤销和重做功能:命令模式天然支持撤销和重做操作。通过保存命令对象的历史记录,可以轻松地实现撤销和重做功能。例如文本编辑器中的撤销和重做操作,可以通过将每个编辑操作封装成命令对象,并保存命令对象的历史记录来实现。
-
日志记录:命令模式可以用于记录操作日志。每个命令对象在执行时可以记录相应的操作信息,包括执行时间、执行者、参数等。这样可以方便地跟踪和审计系统的操作日志。
-
远程方法调用:命令模式可以应用于远程方法调用(RPC)场景。将请求参数封装成命令对象,并通过网络传输给远程服务端执行。这样可以实现跨进程或跨网络的方法调用,并支持异步调用、故障恢复等功能。
代码示例
package main
import "fmt"
// Command 命令接口
type Command interface {
Execute()
Undo()
}
// AddCommand 具体命令 - 加法操作
type AddCommand struct {
receiver *Receiver
value int
}
// Execute 执行命令
func (a *AddCommand) Execute() {
a.receiver.Add(a.value)
}
// Execute 执行命令
func (a *AddCommand) Undo() {
a.receiver.Subtract(a.value)
}
// SubtractCommand 具体命令 - 减法操作
type SubtractCommand struct {
receiver *Receiver
value int
}
// Execute 执行命令
func (s *SubtractCommand) Execute() {
s.receiver.Subtract(s.value)
}
// Undo 撤销命令
func (s *SubtractCommand) Undo() {
s.receiver.Add(s.value)
}
// Receiver 接收者
type Receiver struct {
value int
}
// Add 执行加法操作
func (r *Receiver) Add(value int) {
r.value += value
fmt.Println("加法,结果为:", r.value)
}
// Subtract 执行减法操作
func (r *Receiver) Subtract(value int) {
r.value -= value
fmt.Println("减法,结果为:", r.value)
}
// Invoker 调用者
type Invoker struct {
commands []Command
}
// ExecuteCommand 执行命令
func (i *Invoker) ExecuteCommand(command Command) {
command.Execute()
i.commands = append(i.commands, command)
}
// UndoCommand 撤销命令
func (i *Invoker) UndoCommand() {
if len(i.commands) == 0 {
return
}
// 获取最后一次执行的命令
command := i.commands[len(i.commands)-1]
// 撤销命令
command.Undo()
// 移除最后一次执行的命令
i.commands = i.commands[:len(i.commands)-1]
}
func main() {
reveiver := &Receiver{}
invoker := &Invoker{}
// 执行加法操作
invoker.ExecuteCommand(&AddCommand{receiver: reveiver, value: 10})
// 执行减法操作
invoker.ExecuteCommand(&SubtractCommand{receiver: reveiver, value: 5})
// 撤销最后一次命令
invoker.UndoCommand()
// 再次撤销命令
invoker.UndoCommand()
}
加法,结果为: 10
减法,结果为: 5
加法,结果为: 10
减法,结果为: 0
代码中定义了两个具体命令类AddCommand和SubtractCommand,分别用于执行加法和减法操作。这两个具体命令类实现了Command接口,并分别实现了Execute和Undo方法。Receiver是接收者类,负责执行具体的操作。Invoker是调用者类,负责执行命令和撤销命令。客户端通过调用者对象invoker来执行命令。