【Go-设计模式】命令模式 详解

shadowLogo

概念:

命令模式 本质是把 某个对象的方法调用 封装到对象中,方便 传递存储调用

在软件设计中,我们经常需要向 某些对象 发送请求,但是并 不知道请求的接收者 是谁,也 不知道被请求的操作 是哪个,只需在程序运行时指定 具体的请求接收者 即可
命令模式使得 请求发送者请求接收者 消除彼此之间的 耦合,让 对象之间的 调用关系 更加 灵活,实现 解耦

在命令模式中,会将 一个请求 封装为 一个对象,以便使用 不同参数 来表示 不同的请求(即命名),同时 命令模式 也支持 可撤销的操作

通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)

命令


UML:

uml

  • Invoker
    调用者 角色,客户端通过 Invoker 调用系统功能
  • Receiver
    接受者 角色,知道如何实施和执行一个请求相关的操作
  • Command
    命令 角色,需要执行的 所有命令 都在这里,可以是接口或抽象类
  • ConcreteCommand
    具体的命令,将 一个接受者对象一个动作 绑定,调用、接受者相应的操作,实现 execute

示例:

命令接口:

type Command interface {
	Execute()
	Undo()
}

具体的命令:

兜底 无效命令:

type NoCommand struct {
}

func (this NoCommand) Execute() {
	fmt.Println("==========[NoCommand] Execute nothing!==========")
}

func (this NoCommand) Undo() {
	fmt.Println("==========[NoCommand] Undo nothing!==========")
}

具体命令1:

type ConcreteOnCommand1 struct {
	receive Command1Receiver
}

func (this ConcreteOnCommand1) Execute() {
	fmt.Println("========== ConcreteOnCommand1 on Execute! ==========")
	this.receive.On()
}

func (this ConcreteOnCommand1) Undo() {
	fmt.Println("========== ConcreteOnCommand1 on Undo! ==========")
	this.receive.Off()
}

具体命令2:

type ConcreteOffCommand1 struct {
	receive Command1Receiver
}

func (this ConcreteOffCommand1) Execute() {
	fmt.Println("========== ConcreteOnCommand1 off Execute! ==========")
	this.receive.Off()
}

func (this ConcreteOffCommand1) Undo() {
	fmt.Println("========== ConcreteOnCommand1 off Undo! ==========")
	this.receive.On()
}

具体命令3:

type ConcreteOnCommand2 struct {
	receive Command1Receiver
}

func (this ConcreteOnCommand2) Execute() {
	fmt.Println("========== ConcreteCommand2 Execute! ==========")
	this.receive.On()
}

func (this ConcreteOnCommand2) Undo() {
	fmt.Println("========== ConcreteCommand2 Undo! ==========")
	this.receive.Off()
}

具体命令4:

type ConcreteOffCommand2 struct {
	receive Command1Receiver
}

func (this ConcreteOffCommand2) Execute() {
	fmt.Println("========== ConcreteCommand2 Execute! ==========")
	this.receive.Off()
}

func (this ConcreteOffCommand2) Undo() {
	fmt.Println("========== ConcreteCommand2 Undo! ==========")
	this.receive.On()
}

命令接收者:

命令接收者1:

type Command1Receiver struct {
}

func (this Command1Receiver) On() {
	fmt.Println("Command1Receiver is On now!")
}

func (this Command1Receiver) Off() {
	fmt.Println("Command1Receiver is Off now!")
}

命令接收者2:

type Command2Receiver struct {
}

func (this Command2Receiver) On() {
	fmt.Println("Command2Receiver is On now!")
}

func (this Command2Receiver) Off() {
	fmt.Println("Command2Receiver is Off now!")
}

命令调用者:

type RemoteController struct {
	// 开 按钮的命令数组
	onCommands  map[int64]Command
	offCommands map[int64]Command

	// 执行撤销的命令
	undoCommand Command

	// 模拟 布隆过滤器
	defaultCommand NoCommand
}

func (this *RemoteController) SetCommand(no int64, onCommand, offCommand Command) {
	this.onCommands[no] = onCommand
	this.offCommands[no] = offCommand
}

func (this *RemoteController) TriggerOnButton(no int64) {
	if _, exsit := this.onCommands[no]; !exsit {
		this.defaultCommand.Execute()
		this.undoCommand = this.defaultCommand
	} else {
		this.onCommands[no].Execute()
		this.undoCommand = this.onCommands[no]
	}
}

func (this *RemoteController) TriggerOffButton(no int64) {
	if _, exsit := this.offCommands[no]; !exsit {
		this.defaultCommand.Execute()
		this.undoCommand = this.defaultCommand
	} else {
		this.offCommands[no].Execute()
		this.undoCommand = this.offCommands[no]
	}
}

func (this *RemoteController) TriggerUndoButton() {
	this.undoCommand.Undo()
}

func NewRemoteController() RemoteController {
	return RemoteController{
		onCommands:  map[int64]Command{},
		offCommands: map[int64]Command{},
	}
}

测试:

package main

import "DemoProject/design/base23/command"	// 根据自己的项目路径定制

func main() {
	// command
	controller := command.NewRemoteController()

	onCommand1 := command.ConcreteOnCommand1{}
	offCommand1 := command.ConcreteOffCommand1{}
	controller.SetCommand(1, onCommand1, offCommand1)

	onCommand2 := command.ConcreteOnCommand2{}
	offCommand2 := command.ConcreteOffCommand2{}
	controller.SetCommand(2, onCommand2, offCommand2)

	controller.TriggerOnButton(1)
	controller.TriggerOffButton(2)
	controller.TriggerUndoButton()

	controller.TriggerOnButton(10086)
}

注意事项:

发起请求的对象执行请求的对象 解耦
发起请求的对象调用者调用者 只要 调用命令对象的 execute()方法 就可以让 接收者 工作,而 不必知道具体的接收者对象是谁、是如何实现 的,命令对象 会负责让 接收者 执行请求的动作,
也就是说:”请求发起者” 和 “请求执行者” 之间的 解耦 是通过 命令对象 实现的,命令对象起到了 纽带桥梁 的作用。

可以设计一个 命令队列,只要把 命令对象 放到 队列,就可以 多线程执行命令

可以实现对 请求撤销重做

命令模式 不足:可能导致 某些系统有 过多的具体命令类增加了系统的复杂度,这点在在使用的时候要注意

空命令 也是一种 设计模式,它为我们 省去了判空的操作
在上面的实例中,如果没有用空命令,我们 每按下一个按键 都要 判空,这给我们编码带来一定的麻烦

经典应用场景:界面的 一个按钮 都是 一条命令,模拟 CMD(DOS 命令)撤销/恢复触发- 反馈 机制

posted @ 2021-12-15 08:45  在下右转,有何贵干  阅读(103)  评论(0)    收藏  举报