[go] 命令模式

命令模式

  • 将"请求"封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

    模型说明

  • 触发者类负责对请求进行初始化,其中必须包含一个成员变量来存储对于命令对象的引用。触发命令,而不同接受者直接发送请求。注意,发送者并不负责创建命令对象:它通常会通过构造函数从客户端处获得预先生成的命令。

  • 命令接口通常仅声明一个执行命令的方法。

  • 具体命令会实现各种类型的请求。具体命令自身并不完成工作,而是会将调用委派给一个业务逻辑对象。但为了简化代码,这些类可以进行合并。

  • 接收对象执行方法所需的参数可以声明为具体命令的成员变量。可以将命令对象设为不可变,仅允许通过构造函数对这些成员变量进行初始化。

  • 接收者类包含部分业务逻辑。几乎任何对象都可以作为接收者。绝大部分命令只处理如何将请求传递到接收者的细节,接收者自己会完成实际的工作。

  • 客户端会创建并配置具体命令对象。客户端必须将包括接收者实体在哪的所有请求参数传递给命令的构造函数。此后,生成的命令就可以与一个或多个发送者相关联。

优缺点

1.优点

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

2.缺点

  • 代码可能会变得更加复杂, 因为你在发送者和接收者之间增加了一个全新的层次。

使用场景

  • 如果你需要通过操作来参数化对象, 可使用命令模式。
  • 如果你想要将操作放入队列中、 操作的执行或者远程执行操作, 可使用命令模式。
  • 如果你想要实现操作回滚功能, 可使用命令模式。

参考代码

go 复制代码
// command.go
// 命令接口
type command interface {
	execute()
	undo()
}
go 复制代码
// concrete_command.go
// 具体命令
type light struct{}

func (l *light) label() string {
	return "light"
}

func (l *light) on() {
	fmt.Println("light is on")
}

func (l *light) off() {
	fmt.Println("light is off")
}

type lightOnCommand struct {
	l *light
}

func (c *lightOnCommand) execute() {
	c.l.on()
}

func (c *lightOnCommand) undo() {
	c.l.off()
}

type lightOffCommand struct {
	l *light
}

func (c *lightOffCommand) execute() {
	c.l.off()
}

func (c *lightOffCommand) undo() {
	c.l.on()
}

const (
	labelNoCommand = "on_command"
)

type noCommand struct{}

func (n *noCommand) execute() {
	fmt.Println("no command executed")
}

func (n *noCommand) undo() {
	fmt.Println("no command undo")
}
go 复制代码
// invoker.go
// 触发者
type remoteController struct {
	onCommands  map[string]command
	offCommands map[string]command
	preCmd      command
}

func createSimpleRemoteController() *remoteController {
	rc := &remoteController{
		onCommands:  make(map[string]command),
		offCommands: make(map[string]command),
	}
	// 这里处理为了没有找到插槽时的表现
	noCMD := new(noCommand)
	rc.onCommands[labelNoCommand] = noCMD
	rc.offCommands[labelNoCommand] = noCMD

	// 初始化的时候,没有上一次点击的按钮,所以使用 noCommand
	rc.preCmd = noCMD
	return rc
}

func (rc *remoteController) setCommand(label string, onCMD, offCMD command) {
	rc.onCommands[label] = onCMD
	rc.offCommands[label] = offCMD
}

func (rc *remoteController) onButtonWasPressed(label string) {
	cmd := rc.onCommands[label]
	if cmd == nil {
		cmd = rc.onCommands[labelNoCommand]
	}
	cmd.execute()
	rc.preCmd = cmd
}

func (rc *remoteController) offButtonWasPressed(label string) {
	cmd := rc.offCommands[label]
	if cmd == nil {
		cmd = rc.offCommands[labelNoCommand]
	}
	cmd.execute()
	rc.preCmd = cmd
}

func (rc *remoteController) undo() {
	rc.preCmd.undo()
}
go 复制代码
// main.go
// 客户端
func main() {
	simpleLight := new(light)
	lightOnCmd := &lightOnCommand{l: simpleLight}
	lightOffCmd := &lightOffCommand{l: simpleLight}

	rc := createSimpleRemoteController()
	rc.setCommand(simpleLight.label(), lightOnCmd, lightOffCmd)

	rc.onButtonWasPressed(simpleLight.label())
	rc.offButtonWasPressed(simpleLight.label())
	rc.undo()

	rc.onButtonWasPressed("coffeeMachine")
}

输出:

go 复制代码
light is on 
light is off 
light is on 
no command executed
相关推荐
MATLAB代码顾问21 分钟前
多种群协同进化算法(MPCE)求解大规模作业车间调度问题——附MATLAB代码
开发语言·算法·matlab
代码小书生1 小时前
statistics,一个统计的 Python 库!
开发语言·python
摇滚侠1 小时前
整洁的桌面和任务栏 Java 开发工程师提效方法
java·开发语言
知识分享小能手1 小时前
R语言入门学习教程,从入门到精通,R语言数据计算与分组统计(9)
开发语言·学习·r语言
山居秋暝LS1 小时前
安装C++版opencv和opencv_contrib
开发语言·c++·opencv
老陈说编程2 小时前
12. LangChain 6大核心调用方法:invoke/stream/batch同步异步全解析,新手也能轻松学会
开发语言·人工智能·python·深度学习·机器学习·ai·langchain
014-code2 小时前
Java 并发中的原子类
java·开发语言·并发
alphageek82 小时前
Matlab linspace函数完全指南:从基础用法到进阶技巧
开发语言·其他·matlab
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式
善恶怪客2 小时前
Java-数组和可变参数
java·开发语言