[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
相关推荐
芝士就是力量啊 ೄ೨4 小时前
Python如何编写一个简单的类
开发语言·python
逻辑驱动的ken4 小时前
Java高频面试考点18
java·开发语言·数据库·算法·面试·职场和发展·哈希算法
MoonBit月兔4 小时前
「Why MoonBit 」第一期——Singularity Note AI 学习助手
开发语言·人工智能·moonbit
木木_王4 小时前
嵌入式Linux学习 | 数据结构 (Day05) 栈与队列详解(原理 + C 语言实现 + 实战实验 + 易错点剖析)
linux·c语言·开发语言·数据结构·笔记·学习
冷雨夜中漫步4 小时前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
超龄编码人4 小时前
Qt Widgets Designer QTabWidget无法添加布局
开发语言·qt
直奔標竿4 小时前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
Python大数据分析@4 小时前
CLI一键采集,使用Python搭建TikTok电商爬虫Agent
开发语言·爬虫·python
@小码农5 小时前
2026年3月Scratch图形化编程等级考试一级真题试卷
开发语言·数据结构·c++·算法
这儿有一堆花5 小时前
住宅代理(Residential Proxy)技术指南
开发语言·数据库·php