深入理解23种设计模式(13) -- 命令模式

在这里插入图片描述

介绍

  1. 命令模式 ( Command Pattern) : 在软件设计中,我们经常需要向某些对象发送请求,但是并布置的请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要程序运行时指定具体的请求接受者即可,此时,可以使用命令模式来进行设计
  2. 命令模式使得请求发送者与请求接收者消标题 除彼此间的耦合,让对象之间调用关系更加灵活,实现解耦
  3. 在命令模式中,会将一个请求封装为一个对象,以便使用不同的参数来表示不同的请求(即命名),同时命令模式页支持可撤销的操作
  4. 理解:将军发布命令、士兵去执行、其中 将军(命令发布者) 、士兵(命令具体执行者)、命令(连接将军和士兵)

在这里插入图片描述

  • Command : 定义命令的接口,声明执行的方法。
  • ConcreteCommand: 命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作
  • Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • Invoker : 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

案例:

  1. 我们买了一套智能家电,又照明灯、风扇、冰箱、洗衣机,我们手机安装app就可以控制这些家电工作
  2. 这些智能家电来自不同的厂家,我们不想针对每一个厂家都安装一个APP,我们希望一个APP控制全部家电
  3. 要实现一个APP控制所有智能家电,则每个智能厂家都需要提供一个统一都接口给app调用,这时就可以考虑命令模式
  4. 命令模式可以将 "动作的请求" 者 从 "动作的执行者" 对象解析出来

实现:

  1. 创建 Command 接口
public interface Command {

    /**
     * 执行动作
     */
    public void execute();

    /**
     * 撤销动作
     */
    public void undo();
}
  1. 实现 Command 接口 LightOffCommand 类
public class LightOffCommand implements Command{

    LightReceiver lightReceiver;

    //构造器
    public LightOffCommand(LightReceiver lightReceiver) {
        this.lightReceiver = lightReceiver;
    }

    @Override
    public void execute() {
        //调用接收者的方法
        lightReceiver.off();
    }

    @Override
    public void undo() {
        //调用接收者的方法
        lightReceiver.on();
    }
}

  1. 实现 Command 接口 LightOnCommand 类
public class LightOnCommand implements Command{


    LightReceiver lightReceiver;

    //构造器
    public LightOnCommand(LightReceiver lightReceiver) {
        this.lightReceiver = lightReceiver;
    }

    @Override
    public void execute() {
        //调用接收者的方法
        lightReceiver.on();
    }

    @Override
    public void undo() {
        //调用接收者的方法
        lightReceiver.off();
    }
}

  1. 接受者 LightReceiver
public class LightReceiver {

    public void on(){
        System.out.println("电灯打开了");
    }

    public void off(){
        System.out.println("电灯关闭了");
    }
}
  1. 做一个空实现,用于初始化
public class Nocommand implements Command{

    @Override
    public void execute() {

    }

    @Override
    public void undo() {

    }
}

  1. 执行者
public class RemoteController {

    Command[] onCommands;

    Command[] offCommands;

    Command undoCommond;

    public RemoteController() {
        onCommands =new Command[5];
        offCommands =new Command[5];
        for (int i = 0; i < 5; i++) {
            onCommands[i] = new Nocommand();
            offCommands[i] = new Nocommand();
        }
    }

    /**
     * 设置成命令
     * @param no
     * @param onCommand
     * @param offCommand
     */
    public void setCommand(int no,Command onCommand,Command offCommand){
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }


    /**
     * 按下开机按钮
     * @param no
     */
    public void onButtonWasPushed(int no){
        //执行
        onCommands[no].execute();
        //记录操作用于撤销
        undoCommond = onCommands[no];
    }

    /**
     * 按下关机按钮
     * @param no
     */
    public void offButtonWasPushed(int no){
        //执行
        offCommands[no].execute();
        //记录操作用于撤销
        undoCommond = offCommands[no];
    }

    /**
     * 按下关机按钮
     * @param no
     */
    public void undoButtonWasPushed(int no){
        undoCommond.undo();
    }
}
  1. 客户端
public class Client {

    public static void main(String[] args) {
        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.onButtonWasPushed(0);
        System.out.println("按下灯的关按钮");
        remoteController.offButtonWasPushed(0);
        System.out.println("按下灯撤销按钮");
        remoteController.undoButtonWasPushed(0);
    }
}

在这里插入图片描述

扩展

命令模式在Spring源码中使用 query 查询

@Autowired
    private JdbcTemplate jdbcTemplate;

    public void t1(){
        jdbcTemplate.queryForObject("",类.Class);
    }

点进去 会发现源码中调用

    @Nullable
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Executing SQL query [" + sql + "]");
        }

        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
            QueryStatementCallback() {
            }

            @Nullable
            public T doInStatement(Statement stmt) throws SQLException {
                ResultSet rs = null;

                Object var3;
                try {
                    rs = stmt.executeQuery(sql);
                    var3 = rse.extractData(rs);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                }

                return var3;
            }

            public String getSql() {
                return sql;
            }
        }

        return this.execute((StatementCallback)(new QueryStatementCallback()));
    }

这里面有个内部类 QueryStatementCallback 实现了 StatementCallback 接口

而这个接口只有一个方法

	@Nullable
    T doInStatement(Statement stmt) throws SQLException, DataAccessException;

StatementCallback 接口的实现 在这里插入图片描述 然后我们看看这个类具体实现方法 doInStatement 本质上就是调用这个方法的参数的 executeQuery 方法 执行sql。

最后就是创建了这个内部类的实例传给 execute方法 ,点进去

@Nullable
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
        Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
        Statement stmt = null;

        Object var11;
        try {
            stmt = con.createStatement();
            this.applyStatementSettings(stmt);
            T result = action.doInStatement(stmt);
            this.handleWarnings(stmt);
            var11 = result;
        } catch (SQLException var9) {
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, this.getDataSource());
            con = null;
            throw this.translateException("StatementCallback", sql, var9);
        } finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, this.getDataSource());
        }

        return var11;
    }

1)StatementCallBack 接口,类似命令接口(Command)

2)class QueryStatementCallBack implement StatementCallBack,SqlProvider,匿名内部类,实现了命令接口,同时也充当命令接收者;

3)命令调用者是 JdbcTemplate ,其中 execute(StatementCallback action) 方法中,调用 action.doInStatement() 方法,不同的实现 StatementCallback 接口的对象,对应不同的 doInStatement 实现逻辑;



github Demo地址 : ~~~传送门~~~

个人博客地址:http://blog.yanxiaolong.cn/

end
  • 作者:yxl(联系作者)
  • 发表时间:2021-02-21 12:23
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论