将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
今天一大早就来了图书馆?,刚坐下来就迫不及待的开始看命令模式的相关资料?。不过这个模式跟我之前的理解出入特别大。
最开始的时候,我以为的命令模式就是函数回调
。但后来发现并不是,但他们两个确实是有关系,这一切的答案都藏在 GOF 的设计模式一书中。
在软件设计模式之始 GOF 的原著中,命令模式的讲解还是在他们开发的那个编辑工具中,其用来讲解的案例就是我们日常编辑使用的编辑工具中,在工具栏有很多个功能按钮,或者菜单按钮。 就比如编辑工具中的一个 新增文件
的按钮?吧。GOF 要表达的意思就是,这个 新增文件
对系统本身来讲就是给使用者提供的一个命令,我们在用的过程中可以给编辑器发送不同的命令,但是这个 新增文件
的操作并不是在这个按钮上实现的,同时对于我们发送命令的人来说,也不知道具体这个 新增文件
这个动作是由谁来执行、怎么执行,这对我们来讲完全是透明的。
我们先不讨论这样做的好处,先看下这里面要说的几个角色
新增文件
按钮(调用新增文件操作命令)我试着按照这个结构写了一下这个代码
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)讨论,或者直接进群交流!天凉了??,进群一起取暖也好啊?,等你~