Skip to content

命令模式:如果把请求变成一个对象,在一些场景更好用!

lvgo edited this page Dec 3, 2020 · 2 revisions

命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。

前言

今天一大早就来了图书馆🏫,刚坐下来就迫不及待的开始看命令模式的相关资料📚。不过这个模式跟我之前的理解出入特别大。

最开始的时候,我以为的命令模式就是函数回调。但后来发现并不是,但他们两个确实是有关系,这一切的答案都藏在 GOF 的设计模式一书中。

开始学习

在软件设计模式之始 GOF 的原著中,命令模式的讲解还是在他们开发的那个编辑工具中,其用来讲解的案例就是我们日常编辑使用的编辑工具中,在工具栏有很多个功能按钮,或者菜单按钮。 就比如编辑工具中的一个 新增文件 的按钮🆕吧。GOF 要表达的意思就是,这个 新增文件 对系统本身来讲就是给使用者提供的一个命令,我们在用的过程中可以给编辑器发送不同的命令,但是这个 新增文件 的操作并不是在这个按钮上实现的,同时对于我们发送命令的人来说,也不知道具体这个 新增文件 这个动作是由谁来执行、怎么执行,这对我们来讲完全是透明的。

我们先不讨论这样做的好处,先看下这里面要说的几个角色

  1. 客户端应用
  2. 新增文件按钮(调用新增文件操作命令)
  3. 操作命令
  4. 操作接收(负责具体的操作执行)

我试着按照这个结构写了一下这个代码

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();
    }
}
  1. 客户端应用 Client
  2. 新增文件按钮(调用新增文件操作)Invoker
  3. 操作命令 AddFileCommand
  4. 操作接收者 FileReceiver
新增文件

关于 ‘命令’ 的疑惑 🤔

按照上面的方式实现下来,我有一种感觉,有种脱裤子放屁的感觉,我直接调用 FileReceiver 不香吗

非要这样

command.png

我以为,使用者利用按钮直接调用对应的操作不就行了吗?就像我下面这样中间非要放一个命令对象(将具体的请求包装成了这个对象)?

command-no-command

解惑 ‘命令’ 🤪

不过不久我就找到了答案💡

首先看下命令模式要解决的问题❔:对请求排队、下载或记录请求日志,以及支持可撤消的操作。

然后我们开始思考🤔如果没有中间这个 “命令” 角色,那这些功能做在哪里?只能做在接收者,也就是逻辑具体的实现里面,那这是不是违背了一个设计原则,叫做 单一职责原则 ?而且对这种 ”辅助型“ 的功能变多会导致逻辑实现类变得越来越”肿胀“,没错,就是”肿胀“!

并且这也使得调用者和实现者之间通过这个“命令”进行解耦,然后我们使用依赖倒置原则,将“命令”提取出来一个抽象类,这使得扩展请求也变得容易了。而且对于高层模块来说,自己完全不需要关心调用的时候具体的请求内容和实现内容,通过“命令”来完成自己的操作,比如点一个按钮、遥控器下的按键(从这里还可以看出,多个命令可以对应一个接受者,比如数字键的换台)、去餐厅点菜。这样一看,命令模式还真是符合这种设计思路的命名啊。

命令模式类图 📌

command-UML

主要结构

  1. 调用者,也是暴露给客户端的对象 Invoker
  2. 命令接口,Command(满足依赖倒置原则,便于扩展)
  3. 具体的命令,这里要包含谁来接受这个命令的接受者对象 ConcreteCommand
  4. 命令的接收者,这里没有列实现类是因为任何类都可以是接收者 Receiver

代码 📃

命令模式这篇使用的是通用框架写了一个实现,在这基础上事实上我们可以做很多扩展,比如再 Invoker 类中将 command 换成 List<Command> 来实现请求的排队、撤销等操作。

image-20201128142121299

总结 📚

适用场景:

  1. 需要记录请求记录;
  2. 请求可以进行排队处理;
  3. 请求可以进行撤销、重做;
  4. 具体接收者来决定请求是否执行(关于这一点,如果请求不是封装成一个对象的话,判断起来是比较困难的)

不过这种模式并不是一个常用的思想,一定是当你想要对请求做一些事情的时候才考虑,具体的事情就上面提到的 4 点,不然的话使用这种模式真的就是我上面说的,“脱裤子放屁了”。

最后再来一句话来总结一下命令模式,“张三,把门关一下”。这里我就是 Invoker,“把门关一下“ 就是 command (命令),“张三” 是 receiver (接收者)。更多时候,我们实际开发中,”把门关一下“ 都是定义好的,”我“直接选就行了,就像遥控器上的按键一样。但切记这个模式的使用时机,别做”恶心“人的事!

如果哪里有问题或者有疑问,欢迎加我微信(lvgocc)讨论,或者直接进群交流!天凉了🥶🥶,进群一起取暖也好啊😁,等你~

目录

首页_HOME

1. 单例模式: 资源!要合理的分配使用!

2. 原型模式:啥?盗图、盗文章的人居然用的是一种设计模式!原型模式?

3. 工厂模式:像工厂一样创建对象,让业务代码更专注!

4. 抽象工厂模式:抽象工厂模式和工厂模式有区别吗?

5. 建造者模式:学个设计模式还和人生扯上关系了?

6. 代理模式:有什么问题跟我律师说吧!

7. 装饰者模式:玩了把坦克大战居然彻底搞懂了装饰者模式!

8. 桥接模式:这个不常用的设计模式居然被我学的最透,草率了!

9. 适配器模式:今天轻松点,就说说什么是“榫”,什么是“卯”,什么是“榫卯”!

10. 外观模式:书生的家书是谁送的?书童到底是个什么角色?

11. 享元模式:如果让你开发英雄联盟的兵线,你会怎么设计?

12. 组合模式:使用组合模式做一个简单的推荐功能

13. 策略模式:学习JDK的比较器架构是如何设计的

14. 模板方法模式:你知道AQS它是干什么的吧,那这个框架是怎么设计的呢?

15. 观察者模式:原来观察者模式是JDK与生俱来的

16. 责任链模式:“张三为了纪念王二请假的悲催经历想出来的一种设计模式”

17. 备忘录模式:这款游戏你玩过吗?是不是经常”重来“?

18. 迭代器模式:你真的“会”遍历list吗?

19. 命令模式:如果把请求变成一个对象,在一些场景更好用!

20. 状态模式:从工作状态,再到订单状态一点点深入学习状态模式

21. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

22. 访问者模式:是“凡尔赛”让我“认清”了访问者模式!

23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。


个人博客 Star Dust

Clone this wiki locally