什么是命令模式
命令模式是指将一个请求封装为一个对象,从而让我们可以用不同的请求对客户进行参数化。命令模式可以将请求者和接受者解耦,发送请求对象只需要知道如何发送即可。
命令模式主要包含以下几个角色:
Command(抽象命令类):一般是抽象类或者接口,声明了执行请求的方法,通过这些方法可以调用接受者的相关操作。
ConcreteCommand(具体命令类):实现了抽象命令类中声明的方法,对应具体的接受者对象,调用了接受者对象的方法。
Invoker(调用者):即请求发送者,通过命令对象来执行请求。
Receiver(接受者):接收者执行与请求相关的操作,实现了请求的具体业务。
命令模式的优缺点
优点
- 降低了系统耦合性。
- 增加新的命令类,无需修改代码,符合开闭原则。
- 可以比较容易的设计一个命令队列。
缺点
- 会导致系统中存在过多的命令类。
命令模式的应用场景
- 需要将请求者和接收者解耦,使两者不直接交互。
- 需要在不同时间指定请求,将请求排队和执行请求。
- 支持命令的撤销和恢复操作。
- 需要将一组命令组合在一起的。
命令模式的案例
// 命令类接口
public interface Command {
/**
* 执行动作(操作)
*/
void execute();
/**
* 撤销动作(操作)
*/
void undo();
}
// 具体命令类
public class LightOffCommand implements Command {
LightReceiver light;
public LightOffCommand(LightReceiver light) {
super();
this.light = light;
}
@Override
public void execute() {
// 调用接收者的方法
light.off();
}
@Override
public void undo() {
// 调用接收者的方法
light.on();
}
}
public class LightOnCommand implements Command {
LightReceiver light;
public LightOnCommand(LightReceiver light) {
super();
this.light = light;
}
@Override
public void execute() {
//调用接收者的方法
light.on();
}
@Override
public void undo() {
//调用接收者的方法
light.off();
}
}
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
// 命令接收者
public class LightReceiver {
public void on() {
System.out.println(" 电灯打开了.. ");
}
public void off() {
System.out.println(" 电灯关闭了.. ");
}
}
public static void main(String[] args) {
//使用命令设计模式,完成通过遥控器,对电灯的操作
//创建电灯的对象(接受者)
LightReceiver lightReceiver = new LightReceiver();
//创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
//需要一个遥控器
RemoteController remoteController = new RemoteController();
//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("--------按下灯的开按钮-----------");
remoteController.onButtonWasPushed(0);
System.out.println("--------按下灯的关按钮-----------");
remoteController.offButtonWasPushed(0);
System.out.println("--------按下撤销按钮-----------");
remoteController.undoButtonWasPushed();
}

命令模式在源码中的应用
JdbcTemplate
// 命令接口
@FunctionalInterface
public interface StatementCallback<T> {
@Nullable
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
// 具体命令
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
@Override
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
/**
* Callback to execute the statement.
*/
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
@Nullable
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback(), true);
}
@Override
@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 (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
/**
* Callback to execute the query.
*/
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback(), true);
}
@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(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
if (closeResources) {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
......
}