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