Design Pattern - 命令模式
一般执行一个操作的过程, 创建对象, 并调用对象的函数, 函数执行, 返回
比如下面的类图, client直接调用Receiver.action
而命令模式, 抽象出command对象, 并在command对象封装对Receiver.action的调用
而client只负责创建command对象(invocation), 并提交给Invoker(通过setCommand), 而command真正的执行(execution)由Invoker控制
从而实现invocation和invoker的分离和解耦合
当然会问, 直接调用那么简单, 为什么要绕那么大的圈来用command?
a. 简化调用过程, 整个调用过程比较繁杂或调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等
b. 作为"CallBack"在面向对象系统中的替代
c. 支持undo, redo
d. 更关键的是, 命令模式往往会用于异步或并发处理模式
比如, producer和consumer模式, producer可以不断产生command放到queue里面, 然后可以通过consumer来异步的执行, 达到并发和异步
命令模式 (Command), 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
这个模式的基本思路是解耦合和责任单一, “行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的, 所以要使用命令模式来解耦合.
场景, 这个模式在平时用的很多, 编辑器里面很常用的'undo/redo', 就是用的这种模式.
经常举的例子就是点菜, 客人可以直接对厨师进行点菜, 这样做有两点不好
1. 客户和厨师紧耦合, 比如厨师换了, 或菜名换了等等, 客户代码需要跟着改变.
2. 厨师除了要烹饪外, 还需要记录每个客户请求, 当客户比较多时, 且客户请求是会变化的, 换个菜, 退个菜, 这个厨师就忙不过来了, 很容易记错. 这就违反了单一责任原则, 厨师就应该负责烹饪, 其他的事应该有专门的waiter负责.
这就是命令模式, 当你需要对请求者的请求(函数调用)进行记录, undo, redo等操作时, 需要单独抽象出Invoker类来处理, 而不要直接耦合在Receiver中.
上面的例子中, 客户是Client, 服务生是Invoker, 厨师是Receiver, 客户请求是Command
class Receiver //最终命令的执行者, 厨师 { public void Action() { //具体的命令执行, 烹饪菜肴 } } class ConcreteCommand : Command //具体的客户请求, 点一道菜 { protected Receiver rec; //该情况的执行者, 这道菜哪个厨师烧 public Command(Receiver rec) { this.rec = rec; } public void Execute() { rec.Action()//让执行者执行命令 } } class Invoker //Command模式的核心, 客户请求的管理者, 服务生waiter { private List<Command> cmds = new List<Command>(); //用于保存客户请求 public void SetOrder(Command cmd) { if Valid(cmd) //需要判断请求是否合理, 比如是否有这道菜 { cmds.Add(cmd); //增加请求 } } public void CancelOrder(Command cmd) { cmds.Remove(cmd); //删除请求 } public void Execute() //执行所有请求 { for cmd in cmds { cmd.Excute(); } } }
public class Client { public void client(){ //创建接收者 Receiver receiver = new Receiver(); //创建命令对象,设定它的接收者 Command command = new ConcreteCommand(receiver); //创建Invoker,把命令对象设置进去 Invoker invoker = new Invoker(); invoker.SetOrder(command); } }
Invoker具体的实现可能变化多端, 但是本质就是通过Invoke来把客户请求抽象成command, 并且保存和管理commands, 向用户提供如do, redo, undo, rollback等操作, 而这些操作对receiver都是透明的, 这取决于客户和receiver的解耦.
本文章摘自博客园,原文发布日期:2013-11-19
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的