设计模式-23-命令模式

命令模式(Command Pattern)将请求或操作封装为一个对象,从而允许使用不同的请求、队列或日志来参数化其他对象。在这种模式下,命令的调用者与接收者之间解耦,使得调用者不需要知道接收者的具体实现

逻辑结构

在命令模式中,涉及以下几个核心角色:

  1. 命令(Command):命令对象封装了执行特定操作的方法,并在需要时将该操作的接收者(执行者)绑定到命令对象上。
  2. 接收者(Receiver):接收者是命令的实际执行者,负责执行命令所代表的操作。
  3. 调用者(Invoker):调用者负责调用命令对象并触发执行相应的操作。它并不直接知道命令的具体细节,只需知道如何触发命令的执行。
  4. 客户端(Client):客户端创建具体的命令对象,并将命令对象与相应的接收者进行绑定。客户端负责组装命令对象和接收者,并决定命令的执行时间和顺序。
Command

实现代码

代码路径:https://github.com/XBoom/DesignPatterns/tree/main/22_Command

  1. 首先,定义命令接口,封装执行特定操作的方法

    1
    2
    3
    4
    // Command 接口定义了命令对象的执行方法
    type Command interface {
    Execute()
    }
  2. 然后,定义接受接口,命令的实际执行者

    1
    2
    3
    4
    // Receiver 接收者接口
    type Receiver interface {
    Action()
    }
  3. 接着,定义具体的接受者

    1
    2
    3
    4
    5
    6
    // ConcreteReceiver 具体接收者
    type ConcreteReceiver struct{}

    func (cr *ConcreteReceiver) Action() {
    fmt.Println("接收者执行具体操作")
    }
  4. 然后,定义具体命令,将接受者绑定到命令,并进行执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // ConcreteCommand 具体命令
    type ConcreteCommand struct {
    receiver Receiver
    }

    func NewConcreteCommand(receiver Receiver) *ConcreteCommand {
    return &ConcreteCommand{
    receiver: receiver,
    }
    }

    func (cc *ConcreteCommand) Execute() {
    cc.receiver.Action()
    }
  5. 最后,定义调用者负责命令执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // Invoker 调用者
    type Invoker struct {
    command Command
    }

    func NewInvoker(command Command) *Invoker {
    return &Invoker{
    command: command,
    }
    }

    func (i *Invoker) SetCommand(command Command) {
    i.command = command
    }

    func (i *Invoker) ExecuteCommand() {
    i.command.Execute()
    }

运行

1
2
3
4
5
6
7
//1. 命令接受者
receiver := &ConcreteReceiver{}
//2. 命令
command := NewConcreteCommand(receiver)
//3. 命令调用
invoker := NewInvoker(command)
invoker.ExecuteCommand()

结果

1
接收者执行具体操作

适用场景

  1. 菜单和按钮操作:在图形用户界面中,菜单和按钮通常需要与不同的操作关联。使用命令模式,可以将每个操作封装成一个命令对象,然后在菜单和按钮上设置相应的命令,使得菜单和按钮可以触发执行不同的操作。
  2. 撤销和重做:命令模式非常适合实现撤销和重做功能。通过将每个操作封装成一个命令对象,并将命令对象的历史记录保存下来,可以轻松地实现撤销和重做操作,只需按照命令历史记录依次执行即可。
  3. 任务调度和队列处理:命令模式可以用于实现任务调度和队列处理。将每个任务封装成一个命令对象,并将命令对象放入任务队列中,调度器可以按照队列的顺序依次执行命令,从而实现任务的调度和顺序处理。
  4. 日志记录:命令模式可以用于实现日志记录功能。将每个操作封装成一个命令对象,并在执行命令时记录相关的日志信息,可以方便地实现对操作的日志记录和分析。
  5. 事务处理:命令模式可以用于实现事务处理功能。将一系列操作封装成多个命令对象,并在执行命令时进行事务的提交或回滚,从而实现对事务的管理和控制

总的来说,命令模式适用于需要将请求发送者和请求接收者解耦的场景,以及需要支持撤销、重做、任务调度、日志记录和事务处理等功能的场景。通过将操作封装成命令对象,可以使得系统更灵活、可扩展,并提供更好的代码结构和可维护性

总结

  • 单一职责原则。 可以解耦触发和执行操作的类
  • 开闭原则。 可以在不修改已有客户端代码的情况下在程序中创建新的命令
  • 可以实现撤销和恢复功能
  • 可以实现操作的延迟执行
  • 可以将一组简单命令组合成一个复杂命令

参考链接

  1. https://refactoringguru.cn/design-patterns/command
  2. https://lailin.xyz/post/command.html