命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
前言
今天一大早就来了图书馆?,刚坐下来就迫不及待的开始看命令模式的相关资料?。不过这个模式跟我之前的理解出入特别大。
最开始的时候,我以为的命令模式就是函数回调
。但后来发现并不是,但他们两个确实是有关系,这一切的答案都藏在 GOF 的设计模式一书中。
开始学习
在软件设计模式之始 GOF 的原著中,命令模式的讲解还是在他们开发的那个编辑工具中,其用来讲解的案例就是我们日常编辑使用的编辑工具中,在工具栏有很多个功能按钮,或者菜单按钮。 就比如编辑工具中的一个 新增文件
的按钮?吧。GOF 要表达的意思就是,这个 新增文件
对系统本身来讲就是给使用者提供的一个命令,我们在用的过程中可以给编辑器发送不同的命令,但是这个 新增文件
的操作并不是在这个按钮上实现的,同时对于我们发送命令的人来说,也不知道具体这个 新增文件
这个动作是由谁来执行、怎么执行,这对我们来讲完全是透明的。
我们先不讨论这样做的好处,先看下这里面要说的几个角色
- 客户端应用
新增文件
按钮(调用新增文件操作命令)- 操作命令
- 操作接收(负责具体的操作执行)
我试着按照这个结构写了一下这个代码
代码语言:javascript复制public class Client {
public static void main(String[] args) {
FileReceiver fileReceiver = new FileReceiver();
AddFileCommand addFileCommand = new AddFileCommand(fileReceiver);
Invoker invoker = new Invoker();
invoker.setCommand(addFileCommand);
invoker.executeCommand();
}
}
- 客户端应用
Client
新增文件
按钮(调用新增文件操作)Invoker
- 操作命令
AddFileCommand
- 操作接收者
FileReceiver
新增文件
关于 ‘命令’ 的疑惑 ?
按照上面的方式实现下来,我有一种感觉,有种脱裤子放屁的感觉,我直接调用 FileReceiver 不香吗
非要这样
我以为,使用者利用按钮直接调用对应的操作不就行了吗?就像我下面这样中间非要放一个命令对象(将具体的请求包装成了这个对象)?
解惑 ‘命令’ ?
不过不久我就找到了答案?
首先看下命令模式要解决的问题❔:对请求排队、下载或记录请求日志,以及支持可撤消的操作。
然后我们开始思考?如果没有中间这个 “命令” 角色,那这些功能做在哪里?只能做在接收者,也就是逻辑具体的实现里面,那这是不是违背了一个设计原则,叫做 单一职责原则
?而且对这种 ”辅助型“ 的功能变多会导致逻辑实现类变得越来越”肿胀“,没错,就是”肿胀“!
并且这也使得调用者和实现者之间通过这个“命令”进行解耦,然后我们使用依赖倒置原则,将“命令”提取出来一个抽象类,这使得扩展请求也变得容易了。而且对于高层模块来说,自己完全不需要关心调用的时候具体的请求内容和实现内容,通过“命令”来完成自己的操作,比如点一个按钮、遥控器下的按键(从这里还可以看出,多个命令可以对应一个接受者,比如数字键的换台)、去餐厅点菜。这样一看,命令模式还真是符合这种设计思路的命名啊。
命令模式类图 ?
主要结构
- 调用者,也是暴露给客户端的对象
Invoker
- 命令接口,
Command
(满足依赖倒置原则,便于扩展) - 具体的命令,这里要包含谁来接受这个命令的接受者对象
ConcreteCommand
- 命令的接收者,这里没有列实现类是因为任何类都可以是接收者
Receiver
代码 ?
命令模式这篇使用的是通用框架写了一个实现,在这基础上事实上我们可以做很多扩展,比如再 Invoker
类中将 command 换成 List<Command>
来实现请求的排队、撤销等操作。
总结 ?
适用场景:
- 需要记录请求记录;
- 请求可以进行排队处理;
- 请求可以进行撤销、重做;
- 具体接收者来决定请求是否执行(关于这一点,如果请求不是封装成一个对象的话,判断起来是比较困难的)
不过这种模式并不是一个常用的思想,一定是当你想要对请求做一些事情的时候才考虑,具体的事情就上面提到的 4 点,不然的话使用这种模式真的就是我上面说的,“脱裤子放屁了”。
最后再来一句话来总结一下命令模式,“张三,把门关一下”。这里我就是 Invoker,“把门关一下“ 就是 command (命令),“张三” 是 receiver (接收者)。更多时候,我们实际开发中,”把门关一下“ 都是定义好的,”我“直接选就行了,就像遥控器上的按键一样。但切记这个模式的使用时机,别做”恶心“人的事!
如果哪里有问题或者有疑问,欢迎加我微信(lvgocc)讨论,或者直接进群交流!天凉了??,进群一起取暖也好啊?,等你~