设计模式 - 命令模式详解及其在JdbcTemplate中的应用

基本介绍

在软件设计中,我们经常需要向某些对象发送一些请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

命令模式(Command Pattern)可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。

命令模式将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

模式结构

设计模式 - 命令模式详解及其在JdbcTemplate中的应用

  • Invoker:调用者,发送命令

  • Receiver:接收者,接收命令

  • Command:抽象命令类,定义了所有的命令

  • ConcreteCommand:具体命令类,调用接收者的操作

举例说明

现有许多家电(电灯、电视机、空调....),每个家电都有自己的控制装置,如果需要控制它们,需要逐个开启、逐个关闭,这时候,如果有一个万能遥控器(如下如所示),操作不同的家电只需要按对应的按钮即可,如何使用命令模式实现?

设计模式 - 命令模式详解及其在JdbcTemplate中的应用

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、如果再添加一个电视机,不需要改动任何已有的代码,添加 TvReceiverTvOnCommandTvOffCommand 这几个类即可。

模式分析

?? 优点:

  • 降低系统的耦合度
  • 新的命令可以很容易的加入到系统中
  • 容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令

?? 缺点:

  • 可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

?? 适用环境:

  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • 系统需要在不同的时间指定请求、将请求排队和执行请求。
  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  • 系统需要将一组操作组合在一起,即支持宏命令。

?? 实际应用场景:

  • 界面的一个按钮都是一个命令
  • 模拟CMD(DOS命令)
  • 订单的撤销 / 恢复
  • 触发 - 反馈机制

在 JdbcTemplate 中的应用

在 Spring 的 JdbcTemplate 这个类中有 query() 方法,query() 方法中定义了一个内部类 QueryStatementCallback,QueryStatementCallback 又实现了 StatementCallback 接口,另外还有其它类实现了该接口,StatementCallback 接口中又有一个抽象方法 doInStatement()。在 execute() 中又调用了 query()。

将其关系缕一缕就是:

设计模式 - 命令模式详解及其在JdbcTemplate中的应用

设计模式 - 命令模式详解及其在JdbcTemplate中的应用

上一篇:I P 127.X.X.X 和 0.0.0.0


下一篇:【DataBase】MySQL数据库 03 基本命令 & 语法规范