一、命令模式
1、案例分析
1.1 智能生活项目需求
(1)买一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制这些家电工作
(2)这些智能家电来自不同的厂家,我们不像针对每一种家电都安装一个app,分别控制,只希望有一个app就可以控制全部智能家电
(3)要实现一个app控制所有智能家电的需求,则每个智能家电厂家都要提供一个统一的接口给app调用,这时 就可以考虑命令模式
(4)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来
(5)在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品
2、命令模式基本介绍
(1)命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个。我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来设计。
将军与士兵的例子,比如一个将军有100个士兵,将军发起进攻命令,不需要指定每一个确定的士兵去执行操作,只需要有一个助手,让助手去将这些命令传到给每一个士兵即可。
(2)命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
(3)在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
(4)通俗易懂的理解,将军发布命令,士兵去执行。其中有几个角色
a.将军(命令发布者)
b.士兵(命令的具体执行者)
c.命令(连接将军和士兵)
Inovke是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,聚合接收对象
3、原理类图
(1)Invoke是调用者角色
(2)Command是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
(3)Receiver是接收者角色,知道如何实施和执行一个请求相关的操作
(4)ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute
4、命令模式解决智能生活项目
4.1 思路分析和图解
下图中的撤销是对上一个命令的撤销
4.2 类图
5、代码实例
5.1 Command接口
1 | //创建命令接口 |
5.2 LightReceiver类
1 | public class LightReceiver { |
5.3 LightOnCommand类
实现Command接口
1 | public class LightOnCommand implements Command { |
5.4 LightOffCommand类
实现Command接口
1 | public class LightOffCommand implements Command{ |
5.5 NoCommand类
实现Command接口
1 | //没有任何命令,即空执行:用于初始化每个按钮,当调用空命令时,对象什么都不做 |
5.6 RemoteController类
1 | public class RemoteController { |
5.7 Client类
1 | public class Client { |
结果:
1
2
3
4
5
6 ------按下灯的开按钮-------
电灯打开了...
------按下灯的关按钮-------
电灯关闭了...
------按下撤销按钮-------
电灯打开了...
5.8 添加电视机
5.8.1 TVReceiver类
1 | public class TVReceiver { |
5.8.2 TVOnCommand类
实现Command接口
1 | public class TVOnCommand implements Command { |
5.8.3 TVOffCommand类
实现Command接口
1 | public class TVOffCommand implements Command { |
5.8.4 Client类
1 | public class Client { |
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13 ------按下灯的开按钮-------
电灯打开了...
------按下灯的关按钮-------
电灯关闭了...
------按下撤销按钮-------
电灯打开了...
==========使用遥控器操作电视机=============
------按下电视机的开按钮---------
电视机打开了...
------按下电视机的关按钮---------
电视机关闭了...
-------按下电视机的撤销按钮-------------
电视机打开了...
6、命令模式在Spring框架Jdbc Template应用的源码分析
6.1 JdbcTemplate类
(1)查看JdbcTemplate类
1 | public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {} |
(2)查看query方法(点击JdbcTemplate,ctrl+F12出来右边选项)
(3)首先进入query方法
1 |
|
(4)然后进去query(sql, new RowMapperResultSetExtractor<>(rowMapper))
1 |
|
(5)里面有一个内部类
1 | class QueryStatementCallback implements StatementCallback<T>, SqlProvider |
(6)进StatementCallback查看
1 |
|
(7)查看QueryStatementCallback类 实现StatementCallback
1 |
|
该query又是在JdbcTemplate中使用的,因此JdbcTemplate可以看成为Invoke
(8)查看QueryStatementCallback类 实现StatementCallback
1 |
|
execute的具体代码,也是在JdbcTemplate类中
1 |
|
6.2 角色分析
(1)StatementCallback接口,类似命令接口(Command)
(2)class QueryStatementCallback implements StatementCallback
, SqlProvider,匿名内部类,实现了命令接口,同时也充当命令接收者 class QueryStatementCallback implements StatementCallback
, SqlProvider (3)命令调用者是JdbcTemplate,其中execute(StatementCallback
action, boolean closeResources)方法中,调用action.doInStatement方法,不同的实现StatementCallback接口的对象,对应不同的doInStatement实现逻辑 (4)另外,实现StatementCallback接口的子类还有
7、命令模式的注意事项和细节
(1)将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁,是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
(2)容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令
(3)容易实现对请求的撤销和重做
(4)不足之处:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,尤其需要注意
(5)空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有空命令,我们每按一下一个案件都要判空,这给编码带来一定的麻烦
(6)命令模式经典的应用场景:界面的一个按钮都是一条命令,模拟CMD(DOS命令)、订单的撤销/恢复、触发-反馈机制