Design pattern: behavioral command pattern

Posted by ScottCFR on Tue, 08 Mar 2022 22:27:23 +0100

Project address: https://gitee.com/caochenlei/design-pattern

Chapter 1 Introduction to command mode

Introduction to command mode:

In software development system, there is often a close coupling relationship between "method requester" and "method implementer", which is not conducive to the expansion and maintenance of software functions. For example, it is inconvenient to "undo, redo, record" a method, so "how to decouple the requester and implementer of a method" becomes very important, and the command mode can solve this problem well.

In real life, there are many examples of command mode. For example, when watching TV, we only need to press the remote control to complete the channel switching. This is the command mode, which completely decouples the channel change request from the channel change processing. The TV remote control (command sender) controls the TV (command receiver) through the button (specific command).

Command Pattern is a data-driven design pattern, which belongs to behavioral pattern. The request is wrapped in the object in the form of command and passed to the calling object. The calling object looks for the appropriate object that can handle the command, passes the command to the corresponding object, and the object executes the command.

Advantages of command mode:

  • Reduce the coupling degree of the system by introducing middleware (abstract interface).
  • It has good expansibility and is very convenient to add or delete commands. Using the command mode, adding and deleting commands will not affect other classes, and meet the "opening and closing principle".
  • Macro commands can be implemented. Command mode can be combined with combined mode to assemble multiple commands into a combined command, that is, macro command.
  • It is convenient to realize Undo and Redo operations. The command mode can be combined with the memo mode introduced later to realize the revocation and recovery of commands.
  • Additional functions can be added to the existing commands. For example, logging, combined with decorator mode, will be more flexible.

Disadvantages of command mode:

  • A large number of specific command classes may be generated. The complexity of each type of operation system will increase.
  • The result of command mode is actually the execution result of the receiver. However, in order to structure and decouple the request and implementation in the form of command, additional type structure (the interface between the requester and abstract command) is introduced, which increases the difficulty of understanding. However, this is also a common problem of design patterns. Abstraction will inevitably increase the number of classes. Code separation must be more difficult to understand than code aggregation.

Scenario in command mode:

  • When the request caller needs to be decoupled from the request receiver, the command mode can prevent the caller and receiver from interacting directly.
  • When the system requests commands randomly or often adds or deletes commands, the command mode can easily realize these functions.
  • When the system needs to perform a set of operations, the command mode can define macro commands to realize this function.
  • When the system needs to support Undo and Redo operations of commands, the command object can be stored and implemented in memo mode.

Roles in command mode:

  • Abstract Command class (Command) role: it declares the interface for executing commands and has the abstract method execute() for executing commands.
  • Concrete Command role: it is the concrete implementation class of the abstract command class. It has the receiver object and completes the operation to be executed by calling the receiver's function.
  • Implementer / Receiver role: performs operations related to command functions and is the real implementer of specific command object business.
  • Caller / requester role: it is the sender of the request. It usually has many command objects and executes relevant requests by accessing the command object. It does not directly access the receiver.

Chapter II command mode implementation

1.2 Abstract commands

Command

public interface Command {
    //Execute action (operation)
    public void execute();

    //Undo action
    public void undo();
}

2.2 specific commands

NoCommand

public class NoCommand implements Command {
    @Override
    public void execute() {

    }

    @Override
    public void undo() {

    }
}

TVOnCommand

public class TVOnCommand implements Command {
    TVReceiver tv;

    public TVOnCommand(TVReceiver tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.on();
    }

    @Override
    public void undo() {
        tv.off();
    }
}

TVOffCommand

public class TVOffCommand implements Command {
    TVReceiver tv;

    public TVOffCommand(TVReceiver tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.off();
    }

    @Override
    public void undo() {
        tv.on();
    }
}

2.3 order receiver

TVReceiver

public class TVReceiver {
    public void on() {
        System.out.println(" The TV is on... ");
    }

    public void off() {
        System.out.println(" The TV is off... ");
    }
}

2.4 order requester

RemoteController

public class RemoteController {
    Command[] onCommands;   //Open command set
    Command[] offCommands;  //Off command set
    Command undoCommand;    //Revoke the order

    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();
        }
    }

    //Set command button
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    //Press the on button
    public void onButtonWasPushed(int no) {
        //Find the on button you pressed and call the corresponding method
        onCommands[no].execute();
        //Record this operation for cancellation
        undoCommand = onCommands[no];
    }

    //Press the close button
    public void offButtonWasPushed(int no) {
        //Find the off button you pressed and call the corresponding method
        offCommands[no].execute();
        //Record this operation for cancellation
        undoCommand = offCommands[no];
    }

    //Press the undo button
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}

2.5 final test

Client

public class Client {
    public static void main(String[] args) {
        //Create remote control
        RemoteController remoteController = new RemoteController();
        //Create TV receiver
        TVReceiver tvReceiver = new TVReceiver();
        TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
        TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
        remoteController.setCommand(0, tvOnCommand, tvOffCommand);
        //Call the operation of TV
        remoteController.onButtonWasPushed(0);
        remoteController.offButtonWasPushed(0);
        remoteController.undoButtonWasPushed();
    }
}
 The TV is on... 
 The TV is off... 
 The TV is on... 

Chapter III Application of command mode

The JdbcTemplate of Spring framework uses the command mode, but it is not the standard implementation of the command mode, but uses the idea of the command mode.

StatementCallback acts as an abstract command class.

@FunctionalInterface
public interface StatementCallback<T> {
	@Nullable
	T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}

The following picture shows all the specific command classes that implement StatementCallback.

Here, QueryStatementCallback uses anonymous internal classes instead of being written as a class file. It implements the command interface and also acts as the command receiver.

@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(new QueryStatementCallback(), true);
}

The command caller is JdbcTemplate, where execute (StatementCallback<T> action, Boolean closeResources) method calls action.. Doinstatement (stmt) method. Different objects implementing statementcallback interface correspond to different doInStatemnt implementation logic.

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

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

    }

    return var12;
}

Topics: Design Pattern