Golang - 命令模式(Command Pattern)
命令模式Command Pattern一、模式定义命令模式将一个请求封装为一个对象从而允许你用不同的请求对调用者进行参数化、将请求排队、记录日志以及支持撤销操作。一句话理解把做事变成造东西。不是直接调用light.TurnOn()而是构造一个TurnOnCommand{light}对象让调用者去执行它。这层封装带来了延迟执行、排队、撤销、日志等能力。二、解决什么问题调用者与接收者解耦按钮不知道它控制的是灯还是空调只管执行命令撤销/重做命令对象可以记录执行前的状态支持 Undo请求排队与调度命令对象可以放入队列由调度器按序执行宏命令一组命令打包成一个复合命令一键执行多个操作三、核心角色角色职责Command命令接口声明 Execute 和 Undo 方法ConcreteCommand持有接收者引用实现具体操作Receiver接收者实际执行操作的对象Invoker调用者持有命令引用触发执行Client客户端创建命令并绑定接收者四、结构图┌───────────────┐ ┌──────────────────┐ ┌───────────────┐ │ Invoker │ │ Command │ │ Receiver │ ├───────────────┤ ├──────────────────┤ ├───────────────┤ │ - cmds []Cmd │────│ Execute() │────│ Action() │ │ ExecuteCmd()│ │ Undo() │ └───────────────┘ │ UndoCmd() │ └────────┬─────────┘ └───────────────┘ │ implements ┌──────────┴──────────┐ │ │ ┌──────┴──────┐ ┌───────┴───────┐ │LightOnCmd │ │MacroCommand │ │- light │ │- cmds []Cmd │ │Execute() │ │Execute() │ │Undo() │ │Undo() │ └─────────────┘ └───────────────┘五、Go 实现要点命令接口通常只有两个方法Execute执行和 Undo撤销保持接口最小化函数也是一种命令Go 中func()本身就是零参数零返回的命令用func类型可以省去结构体定义撤销需要保存状态Undo 方法中需要恢复执行前的状态命令对象应在 Execute 时记录必要的快照宏命令用切片组合一个 MacroCommand 内部持有多个 CommandExecute 时依次执行Undo 时逆序撤销六、完整练习代码以智能家居遥控器为场景按钮控制灯、空调、窗帘支持撤销和宏命令。packagemainimportfmt// // 命令接口// // Command 命令接口typeCommandinterface{Execute()Undo()Description()string}// // 接收者各种家居设备// // Light 灯typeLightstruct{namestringisOnboolbrightnessint// 0-100}funcNewLight(namestring)*Light{returnLight{name:name}}func(l*Light)TurnOn(){l.isOntruel.brightness80fmt.Printf( [%s] 开灯亮度 %d%%\n,l.name,l.brightness)}func(l*Light)TurnOff(){l.isOnfalsefmt.Printf( [%s] 关灯\n,l.name)}// AirConditioner 空调typeAirConditionerstruct{namestringisOnbooltemperatureint}funcNewAC(namestring)*AirConditioner{returnAirConditioner{name:name}}func(ac*AirConditioner)TurnOn(){ac.isOntrueac.temperature26fmt.Printf( [%s] 开空调温度 %d°C\n,ac.name,ac.temperature)}func(ac*AirConditioner)TurnOff(){ac.isOnfalsefmt.Printf( [%s] 关空调\n,ac.name)}// Curtain 窗帘typeCurtainstruct{namestringisOpenbool}funcNewCurtain(namestring)*Curtain{returnCurtain{name:name,isOpen:true}}func(c*Curtain)Open(){c.isOpentruefmt.Printf( [%s] 开窗帘\n,c.name)}func(c*Curtain)Close(){c.isOpenfalsefmt.Printf( [%s] 关窗帘\n,c.name)}// // 具体命令实现// // --- 灯开关命令 ---typeLightOnCommandstruct{light*Light// 撤销快照prevIsOnboolprevBrightint}funcNewLightOnCommand(light*Light)*LightOnCommand{returnLightOnCommand{light:light}}func(c*LightOnCommand)Execute(){c.prevIsOnc.light.isOn c.prevBrightc.light.brightness c.light.TurnOn()}func(c*LightOnCommand)Undo(){if!c.prevIsOn{c.light.TurnOff()}else{c.light.isOntruec.light.brightnessc.prevBright fmt.Printf( [%s] 恢复亮度 %d%%\n,c.light.name,c.prevBright)}}func(c*LightOnCommand)Description()string{return开灯}// --- 空调开关命令 ---typeACOnCommandstruct{ac*AirConditioner prevIsOnboolprevTempint}funcNewACOnCommand(ac*AirConditioner)*ACOnCommand{returnACOnCommand{ac:ac}}func(c*ACOnCommand)Execute(){c.prevIsOnc.ac.isOn c.prevTempc.ac.temperature c.ac.TurnOn()}func(c*ACOnCommand)Undo(){if!c.prevIsOn{c.ac.TurnOff()}else{c.ac.isOntruec.ac.temperaturec.prevTemp fmt.Printf( [%s] 恢复温度 %d°C\n,c.ac.name,c.prevTemp)}}func(c*ACOnCommand)Description()string{return开空调}// --- 窗帘关闭命令 ---typeCurtainCloseCommandstruct{curtain*Curtain prevOpenbool}funcNewCurtainCloseCommand(curtain*Curtain)*CurtainCloseCommand{returnCurtainCloseCommand{curtain:curtain}}func(c*CurtainCloseCommand)Execute(){c.prevOpenc.curtain.isOpen c.curtain.Close()}func(c*CurtainCloseCommand)Undo(){ifc.prevOpen{c.curtain.Open()}else{fmt.Printf( [%s] 维持关闭\n,c.curtain.name)}}func(c*CurtainCloseCommand)Description()string{return关窗帘}// --- 宏命令组合多个命令 ---typeMacroCommandstruct{namestringcommands[]Command}funcNewMacroCommand(namestring,commands...Command)*MacroCommand{returnMacroCommand{name:name,commands:commands}}func(m*MacroCommand)Execute(){fmt.Printf( 执行宏命令: %s\n,m.name)for_,cmd:rangem.commands{cmd.Execute()}}func(m*MacroCommand)Undo(){fmt.Printf( 撤销宏命令: %s\n,m.name)// 逆序撤销fori:len(m.commands)-1;i0;i--{m.commands[i].Undo()}}func(m*MacroCommand)Description()string{returnm.name}// // 调用者遥控器// typeRemoteControlstruct{history[]Command// 命令历史用于撤销}funcNewRemoteControl()*RemoteControl{returnRemoteControl{}}// PressButton 按下按钮执行命令func(r*RemoteControl)PressButton(cmd Command){fmt.Printf([遥控器] 执行: %s\n,cmd.Description())cmd.Execute()r.historyappend(r.history,cmd)}// PressUndo 撤销上一次命令func(r*RemoteControl)PressUndo(){iflen(r.history)0{fmt.Println([遥控器] 没有可撤销的操作)return}// 取出最后一条命令lastIdx:len(r.history)-1cmd:r.history[lastIdx]r.historyr.history[:lastIdx]fmt.Printf([遥控器] 撤销: %s\n,cmd.Description())cmd.Undo()}// // 主函数// funcmain(){// 创建设备livingLight:NewLight(客厅灯)bedroomAC:NewAC(卧室空调)livingCurtain:NewCurtain(客厅窗帘)// 创建命令lightOn:NewLightOnCommand(livingLight)acOn:NewACOnCommand(bedroomAC)curClose:NewCurtainCloseCommand(livingCurtain)// 创建宏命令离家模式 关窗帘 开灯 开空调... 不对// 离家模式应该是关灯 关空调 关窗帘// 但我们只有 On 命令让我用宏命令演示回家模式comeHome:NewMacroCommand(回家模式,lightOn,acOn,curClose)// 创建遥控器remote:NewRemoteControl()fmt.Println( 1. 单个命令执行与撤销 )remote.PressButton(lightOn)remote.PressButton(acOn)remote.PressUndo()// 撤销开空调remote.PressUndo()// 撤销开灯fmt.Println(\n 2. 宏命令一键执行 )remote.PressButton(comeHome)fmt.Println(\n 3. 宏命令整体撤销 )remote.PressUndo()// 撤销整个回家模式fmt.Println(\n 4. 撤销空历史 )remote.PressUndo()remote.PressUndo()}七、运行结果 1. 单个命令执行与撤销 [遥控器] 执行: 开灯 [客厅灯] 开灯亮度 80% [遥控器] 执行: 开空调 [卧室空调] 开空调温度 26°C [遥控器] 撤销: 开空调 [卧室空调] 关空调 [遥控器] 撤销: 开灯 [客厅灯] 关灯 2. 宏命令一键执行 [遥控器] 执行: 回家模式 执行宏命令: 回家模式 [客厅灯] 开灯亮度 80% [卧室空调] 开空调温度 26°C [客厅窗帘] 关窗帘 3. 宏命令整体撤销 [遥控器] 撤销: 回家模式 撤销宏命令: 回家模式 [客厅窗帘] 开窗帘 [卧室空调] 关空调 [客厅灯] 关灯 4. 撤销空历史 [遥控器] 没有可撤销的操作 [遥控器] 没有可撤销的操作八、关键设计决策解析1. Undo 的快照策略命令对象在Execute()时保存了接收者执行前的状态快照prevIsOn、prevBright等。Undo 时根据快照恢复。这是命令模式支持撤销的核心机制。有两种快照策略局部快照本实现只保存变化的字段。优点是内存占用小缺点是每个命令类都要手写快照逻辑全量快照在 Execute 前深拷贝整个接收者。优点是通用缺点是内存浪费大2. 宏命令的撤销顺序宏命令 Execute 时正序执行先A后B后CUndo 时必须逆序撤销先C后B后A。这就像穿衣服和脱衣服——穿是先内后外脱是先外后内顺序不能乱。3. 函数式命令的简化当命令不需要 Undo 时Go 可以用func类型大幅简化typeSimpleCommandfunc()func(f SimpleCommand)Execute(){f()}但一旦需要 Undo就必须用结构体保存状态函数式方案就力不从心了。4. 命令模式 vs 观察者模式两者都涉及触发操作但方向不同命令模式调用者 → 命令 → 接收者一对一明确知道发给谁观察者模式被观察者 → 所有观察者一对多不关心具体是谁九、实际项目身影database/sql事务Begin → Exec → Commit/RollbackRollback 就是命令的 UndoHTTP 中间件每个中间件是一个命令按链式顺序执行Git 操作每个 commit 是一个命令revert 就是 Undo文本编辑器每次编辑操作封装为命令CtrlZ 就是 Undo任务队列把命令序列化后放入消息队列Worker 取出后反序列化执行十、小结命令模式的本质是把请求从过程调用变成对象传递。这层封装看似多余实则为延迟执行、排队、撤销、日志、宏操作提供了基础。在 Go 中如果只需 Execute 不需 Undo可以用func简化一旦需要 Undo 或组合就用接口结构体的完整实现。

相关新闻

【财务领域】第五十五篇 企业金融运作、账务操作及运作、会计操作及运作、税务操作和运作、资本运作、账期管理01

【财务领域】第五十五篇 企业金融运作、账务操作及运作、会计操作及运作、税务操作和运作、资本运作、账期管理01

编号 类型 领域 行业 企业性质 企业类型 问题 账款和账务和税-利润管理的数学方程式列表及参数列表及参数的数值范围 关联知识和法律法规和行业标准规定 1 财务管理 账款管理 通用 通用 通用 应收账款周转率计算 方程式:应收账款周转率 = 赊销收入净额 / 平均…

2026/10/18 13:39:01 阅读更多 →
2026 年 7 月汕头代账怎么选避坑|财政局备案代理记账机构名单|汕头代理记账公司口碑推荐|潮荣升

2026 年 7 月汕头代账怎么选避坑|财政局备案代理记账机构名单|汕头代理记账公司口碑推荐|潮荣升

公司简称:潮荣升|服务价格区间:500‑5000 元|实体地址:汕头市潮阳区铜盂镇兴富路与环北路交叉口东北 80 米幸福小区巷口 104 号 服务清单:代理记账、代理报税、税务登记、代理解除税务非正常、注册公司、营…

2026/10/18 4:54:38 阅读更多 →
Unity实时通信:NativeWebSocket库快速集成与实战优化指南

Unity实时通信:NativeWebSocket库快速集成与实战优化指南

1. 项目概述与核心价值 如果你正在开发一个Unity项目,需要实现实时聊天、多人游戏同步、在线排行榜更新或者任何需要服务器与客户端之间保持长连接、双向通信的功能,那么WebSocket几乎是你绕不开的技术选型。传统的HTTP请求(比如UnityWebRequ…

2026/10/19 4:32:20 阅读更多 →

最新新闻

eNSP中AR设备卡在“###”无法启动?一文看懂根因与排查流程

eNSP中AR设备卡在“###”无法启动?一文看懂根因与排查流程

AR设备启动,控制台窗口里只有三个“###”,然后一切静止不动——这个问题在eNSP用户里实在太常见了,做设备模拟实验的基本都撞见过。它不是“再等等就好”的启动变慢,而是一个典型的故障信号。先说明结论:eNSP里跑的AR路…

2026/10/19 4:32:47 阅读更多 →
GET与POST区别及Session、Cookie、Token鉴权实战详解

GET与POST区别及Session、Cookie、Token鉴权实战详解

1. 从一次接口联调翻车说起:为什么GET和POST的区别值得单独拎出来讲刚入行那会儿,我对GET和POST的理解就停留在“GET用来获取数据,POST用来提交数据”这一句话上。直到有一次跟前端联调一个登录接口,前端用GET把用户名和密码拼在U…

2026/10/19 4:32:47 阅读更多 →
【原创AI项目】手把手打造企业级知识库助手:LangChain 1.x + LangGraph + RAG + Agent 全栈

【原创AI项目】手把手打造企业级知识库助手:LangChain 1.x + LangGraph + RAG + Agent 全栈

文章目录 一、开篇:你的企业知识,还在靠"问人"吗? 二、功能全景图 三、系统架构总览 四、项目结构与依赖 五、核心模块一:RAG 检索引擎 5.1 RAG 核心运行流程图 5.2 文档加载:编码容错是个容易踩的坑 5.3 递归切分:为什么用 RecursiveCharacterTextSplitter? …

2026/10/19 4:32:47 阅读更多 →
开源代码审查工具 open-code-review:从规则引擎到 CI 集成的实践指南

开源代码审查工具 open-code-review:从规则引擎到 CI 集成的实践指南

1. 摆脱 Code Review 拖延症:这款开源工具到底解决什么问题说实话,Code Review 这件事在绝大多数团队里都处于一种"人人都知道重要,但就是做不动"的状态。我见过太多项目,PR 一开就是三五天没人理,等发起人催…

2026/10/19 4:32:47 阅读更多 →
打卡信奥刷题(3636)用C++实现信奥题 P11954 「ZHQOI R1」删边

打卡信奥刷题(3636)用C++实现信奥题 P11954 「ZHQOI R1」删边

P11954 「ZHQOI R1」删边 题目描述 给定一个 nnn 个点 mmm 条边的简单无向连通图,点从 111 开始编号。你可以删掉任意条边,使得: 删完后图不连通;删完后图中没有孤立点(即度数为 000 的点)。 构造删边方案或…

2026/10/19 4:32:47 阅读更多 →
3块钱2小时从零训练大模型:消费级显卡也能跑通GPT全流程

3块钱2小时从零训练大模型:消费级显卡也能跑通GPT全流程

最近朋友圈又被一个标题刷屏了:“3块钱,2小时,他用一张显卡从零训练了一个大模型”。说实话,这种标题搁以前我都是直接划走的,因为见过太多拿开源权重微调一下就叫“训练大模型”的玩法。但这次我决定亲手验证一把&…

2026/10/19 4:31:47 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →