命令模式的定义
定义: 将一个请求封装成一个对象, 从而让你使用不同的请求将客户端参数化, 对请求排队或者记录请求日志, 可以提供命令的撤销和恢复功能
通俗的说, 就是当有不同的请求时, 将每一种请求都封装成一个对象, 不同的请求调用不同的执行者来执行
命令模式的通用类图如下:
其中各部分如下:
- Receiver 接收者. 就是干活的角色, 命令传递到这里是应该被执行的, 每一个Receiver对应了一个任务
- Command 命令角色. 需要执行的所有命令在这里声明, 命令角色通过调用Receiver进行命令的执行
- Invoker 调用者. 接收到命令, 并执行命令.
接收者代码:
为什么 Receiver 是一个抽象类呢? 因为接收者可以有多个, 有多个就需要定义一个所有特性的抽象集合.
命令角色是命令模式的核心, 其抽象的命令类代码如下:
根据环境的需求,具体的命令类也可以有多个, 代码如下:
在每个命令类中, 通过构造函数定义了该命令是针对哪一个接收者发出的, 定义一个命令接收的主题.
调用者非常简单, 仅实现命令的传递, 代码如下;
那么,如何调用呢? 场景类代码如下:
至此, 一个简单的命令模式完成
命令模式的应用
命令模式的优点
- 类间解耦. 调用者与接收者之间没有任何依赖关系, 调用者实现功能时只需要调用 Command 抽象类的 execute 方法即可, 不需要了解到底是哪个接收者执行.
- 可扩展性. Command 的子类可以非常容易的扩展, 而调用者Invoker和高层次的模块 Client 不产生严重的代码耦合
- 命令模式结合其他模式会更优秀. 命令模式可以结合责任链模式, 实现命令族解析任务; 结合模板方法模式, 则可以减少Command子类的膨胀问题
命令模式的缺点
Command的子类, 如果有N个命令, 问题就出来了, Command 的子类就是N个了, 这个类膨胀的非常大
命令模式的使用场景
只要认为是命令的地方就可以采用命令模式, 例如: 在GUI开发中, 一个按钮的点击是一个命令,可以采用命令模式; 模拟DOS命令的时候, 当然也要采用命令模式
在上面的代码中, Receiver 暴露给了 Client, 这样其实并不好, 命令模式的Receiver在实际应用中一般都会被封装掉,每一个命令是对一个或多个Receiver的封装, 我们可以在项目中通过有意义的类名或命令名处理命令角色和接收者角色的耦合关系,减少高层模块对低层模块的依赖关系, 提高系统的整体稳定性
修改后的代码如下:
实现了对 接收者的封装, 每个命令完成单一的职责, 而不是根据接收者的不同完成不同的职责, 在高层模块的调用时就不用考虑接收者是谁的问题. 场景类代码如下:
高层模块直接下达命令即可, 完美!!