基本介绍
在软件设计中, 我们经常需要向某些对象发送一些请求, 但是并不知道请求的接收者是谁, 也不知道被请求的操作是哪个, 我们只需要在程序运行时指定具体的请求接收者即可, 此时, 可以使用命令模式来设计, 使得请求发送者与请求接收者消除彼此之间的耦合, 让对象之间的调用关系更加灵活.
命令模式 (Command Pattern) 可以对发送者和接收者完全解耦, 发送者与接收者之间没有直接引用关系, 发送请求的对象只需要知道如何发送请求, 而不必知道如何完成请求.
命令模式将一个请求封装为一个对象, 从而使我们可用不同的请求对客户进行参数化; 对请求排队或者记录请求日志, 以及支持可撤销的操作.
模式结构
Invoker: 调用者, 发送命令
Receiver: 接收者, 接收命令
Command: 抽象命令类, 定义了所有的命令
ConcreteCommand: 具体命令类, 调用接收者的操作
举例说明
现有许多家电(电灯, 电视机, 空调....), 每个家电都有自己的控制装置, 如果需要控制它们, 需要逐个开启, 逐个关闭, 这时候, 如果有一个万能遥控器(如下如所示), 操作不同的家电只需要按对应的按钮即可, 如何使用命令模式实现?
1, 创建电灯的命令接收者
- public class LightReceiver {
- public void on() {
- System.out.println("电灯打开了");
- }
- public void off() {
- System.out.println("电灯关闭了");
- }
- }
2, 创建抽象的命令接口
- public interface Command {
- /**
- * 执行命令
- */
- void execute();
- /**
- * 撤销命令
- */
- void undo();
- }
3, 创建电灯的开启, 关闭命令类, 实现命令接口
- public class LightOnCommand implements Command {
- private LightReceiver lightReceiver;
- public LightOnCommand(LightReceiver lightReceiver) {
- this.lightReceiver = lightReceiver;
- }
- @Override
- public void execute() {
- lightReceiver.on();
- }
- @Override
- public void undo() {
- lightReceiver.off();
- }
- }
- public class LightOffCommand implements Command {
- private LightReceiver lightReceiver;
- public LightOffCommand(LightReceiver lightReceiver) {
- this.lightReceiver = lightReceiver;
- }
- @Override
- public void execute() {
- lightReceiver.off();
- }
- @Override
- public void undo() {
- lightReceiver.on();
- }
- }
4, 创建空的命令类, 用于初始化按钮
- public class NullCommand implements Command {
- @Override
- public void execute() { }
- @Override
- public void undo() { }
- }
5, 创建命令调用者
- public class RemoteController {
- /**
- * 操作对象的个数
- */
- private static final int COUNT = 5;
- /**
- * 开启命令组
- */
- Command[] onCommands;
- /**
- * 关闭命令组
- */
- Command[] offCommands;
- /**
- * 执行撤销的命令
- */
- Command undoCommand;
- /**
- * 初始化
- */
- public RemoteController() {
- onCommands = new Command[COUNT];
- offCommands = new Command[COUNT];
- for (int i = 0; i < COUNT; i++) {
- onCommands[i] = new NullCommand();
- offCommands[i] = new NullCommand();
- }
- }
- /**
- * 设置按钮
- */
- public void setCommand(int no, Command onCommand, Command offCommand) {
- onCommands[no] = onCommand;
- offCommands[no] = offCommand;
- }
- /**
- * 按下开按钮
- */
- public void pressOnButton(int no) {
- onCommands[no].execute();
- // 记录按钮, 以便撤销
- undoCommand = onCommands[no];
- }
- /**
- * 按下关按钮
- */
- public void pressOffButton(int no) {
- offCommands[no].execute();
- // 记录按钮, 以便撤销
- undoCommand = offCommands[no];
- }
- /**
- * 按下撤销按钮
- */
- public void pressUndoButton(int no) {
- if (undoCommand != null) {
- undoCommand.undo();
- }
- }
- }
6, 测试类
- public class Client {
- @Test
- public void testLight() {
- // 创建命令接收者
- LightReceiver lightReceiver = new LightReceiver();
- // 创建电灯的一组操作(开和关)
- LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
- LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
- // 创建命令调用者
- RemoteController remoteController = new RemoteController();
- // 设置命令
- remoteController.setCommand(0, lightOnCommand, lightOffCommand);
- // 执行命令
- System.out.println("---- 按下开灯按钮 ----");
- remoteController.pressOnButton(0);
- System.out.println("---- 按下关灯按钮 ----");
- remoteController.pressOffButton(0);
- System.out.println("---- 按下撤销按钮 ----");
- remoteController.pressUndoButton(0);
- }
- }
7, 运行结果
---- 按下开灯按钮 ----
电灯打开了
---- 按下关灯按钮 ----
电灯关闭了
---- 按下撤销按钮 ----
电灯打开了
8, 如果再添加一个电视机, 不需要改动任何已有的代码, 添加 TvReceiver,TvOnCommand,TvOffCommand 这几个类即可.
模式分析
优点:
降低系统的耦合度
新的命令可以很容易的加入到系统中
容易设计一个命令队列. 只要把命令对象放到队列, 就可以多线程的执行命令
缺点:
可能会导致某些系统有过多的具体命令类. 因为针对每一个命令都需要设计一个具体命令类, 因此某些系统可能需要大量具体命令类, 这将影响命令模式的使用.
适用环境:
系统需要将请求调用者和请求接收者解耦, 使得调用者和接收者不直接交互.
系统需要在不同的时间指定请求, 将请求排队和执行请求.
系统需要支持命令的撤销 (Undo) 操作和恢复 (Redo) 操作.
系统需要将一组操作组合在一起, 即支持宏命令.
实际应用场景:
界面的一个按钮都是一个命令
模拟 CMD(DOS 命令)
订单的撤销 / 恢复
触发 - 反馈机制
在 JdbcTemplate 中的应用
在 Spring 的 JdbcTemplate 这个类中有 query() 方法, query() 方法中定义了一个内部类 QueryStatementCallback,QueryStatementCallback 又实现了 StatementCallback 接口, 另外还有其它类实现了该接口, StatementCallback 接口中又有一个抽象方法 doInStatement(). 在 execute() 中又调用了 query().
将其关系缕一缕就是:
来源: https://www.cnblogs.com/songjilong/p/12776479.html