命令设计模式
1. 简单介绍
命令设计模式(Command)是一种行为型设计模式。该模式将一个请求、与具体的命令执行者封装成不同的对象。这种设计一方面可以对客户端的请求进行参数化,另一方面可以将命令的接收和执行很好的进行解耦。
2. 使用场景
- 系统需要将请求调用者和请求的接收者接口,是的调用者和接收者互补交互
- 系统需要在不同的时间指定请求、将请求排队和执行请求
- 系统需要支持命令的撤销操作(undo)和恢复操作(redo)
- 系统需要将一组操作组合在一起,即支持宏命令
3. 场景举例
程序开发中,经常使用的回调机制可以使用命令模式很好的解决。将回调方法(函数)封装成一个个命令(Command)。程序在回调时,调用相应的命令对象即可。
4. UML类图
5. 具体实现
描述
- 背景:Linux操作系统通过命令控制电脑的开机、重启
- Command:命令封装类
- RebootCommand:具体命令角色,重启命令
- ShutdownCommand: 具体命令角色,关机电脑
- Computer: 电脑,命令的具体执行者
- Invoker:命令的发出者,对应Invoker角色
代码实现
Command.java
/**
* Command:命令封装类
*/
public abstract class Command {
/**
* 聚合命令的具体执行者
*/
protected Computer computer;
public Command(Computer computer) {
this.computer = computer;
}
/**
* 命令执行接口方法
*/
public abstract void execute();
}
RebootCommand.java
/**
* RebootCommand:具体命令角色,重启命令
*/
public class RebootCommand extends Command {
public RebootCommand(Computer computer) {
super(computer);
}
/**
* 重启命令的具体执行交给Computer实例对象
*/
@Override
public void execute() {
computer.restart();
}
}
ShutdownCommand.java
/**
* ShutdownCommand: 具体命令角色,关机电脑
*/
public class ShutdownCommand extends Command {
public ShutdownCommand(Computer computer) {
super(computer);
}
/**
* 关机命令的具体执行交给Computer实例对象
*/
@Override
public void execute() {
computer.shutdown();
}
}
Computer.java
/**
* Computer: 电脑,命令的具体执行者
*/
public class Computer {
/**
* 执行RebootCommand命令
*/
public void restart() {
System.out.println("执行电脑重启命令:电脑正在重启");
}
/**
* 执行ShutdownCommand命令
*/
public void shutdown() {
System.out.println("执行电脑关机命令:电脑正在关机");
}
}
Invoker.java
/**
* Invoker:命令的发出者,对应Invoker角色
*/
public class Invoker {
/**
* 存放待发出命令
*/
private List<Command> commandList = new ArrayList<>();
public void addCommand(Command command) {
commandList.add(command);
}
/**
* 发出执行
*/
public void executeCommands() {
for (Command command : commandList) {
command.execute();
}
}
}
Client.java
/**
* Client: 客户端
*/
public class Client {
public static void main(String[] args) {
// 1. 创建命令具体执行者:电脑
Computer computer = new Computer();
// 2. 创建、并发出命令
Command rebootCommand = new RebootCommand(computer);
Command shutdownCommand = new ShutdownCommand(computer);
// 3. 添加到Invoker中
Invoker invoker = new Invoker();
invoker.addCommand(rebootCommand);
invoker.addCommand(shutdownCommand);
// 3. 执行指令
// 4. 输出结果
// 4.1 执行电脑重启命令:电脑正在重启
// 4.2 执行电脑关机命令:电脑正在关机
invoker.executeCommands();
}
}
7. 源码展示
Spirng
中的JdbcTemplate
从接收SQL
语句到执行SQL
语句过程的实现,采用命令设计模式。其中,StatementCallback
充当命令模式中的Command
角色,其实现类充当ConcreteCommand
角色。不同类型的SQL
语句,会对应不同的StatementCallback
具体实现类,其中包含QueryStatementCallback
、ExecuteStatementCallback
等。JdbcTemplate
类充当Invoker
角色,负责接收待执行的SQL
语句,根据不同的SQL
类型,创建不同类型的StatementCallback
,完成SQL
的执行,真正的执行者是Statement
的实现类,如:ClientStatement
,充当Receiver
角色。
StatementCallback.java
/**
* 充当Command角色
*/
public interface StatementCallback<T> {
// 不负责具体的执行,具体的执行由Statement来完成
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
JdbcTemplate.java
/**
* 充当Invoker角色,负责接收待执行的SQL
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
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 (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
// Command的具体实现,接收到的SQL交给Statement执行
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
// SQL语句的真正执行者Statement,即Rceiver角色
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
// 此种类型,构造QueryStatementCallback命令
// 具体的SQL执行,交给QueryStatementCallback对应的Statement
return execute(new QueryStatementCallback());
}
}
ClientStatement.java
/**
* SQL的具体执行者,对应Receiver角色
*/
public class ClientStatement implements Statement, StatementCallbackInterface {
// SQL的真正的执行方法
public ResultSet executeQuery(String var1) throws SQLException {
try {
synchronized(this.connection_) {
if (this.agent_.loggingEnabled()) {
this.agent_.logWriter_.traceEntry(this, "executeQuery", new Object[]{var1});
}
ClientResultSet var3 = this.executeQueryX(var1);
if (this.agent_.loggingEnabled()) {
this.agent_.logWriter_.traceExit(this, "executeQuery", var3);
}
return var3;
}
} catch (SqlException var6) {
throw var6.getSQLException();
}
}
}