介绍
- 命令模式 ( Command Pattern) : 在软件设计中,我们经常需要向某些对象发送请求,但是并布置的请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要程序运行时指定具体的请求接受者即可,此时,可以使用命令模式来进行设计
- 命令模式使得请求发送者与请求接收者消标题
除彼此间的耦合,让对象之间调用关系更加灵活,实现解耦 - 在命令模式中,会将一个请求封装为一个对象,以便使用不同的参数来表示不同的请求(即命名),同时命令模式页支持可撤销的操作
- 理解:将军发布命令、士兵去执行、其中 将军(命令发布者) 、士兵(命令具体执行者)、命令(连接将军和士兵)
- Command : 定义命令的接口,声明执行的方法。
- ConcreteCommand: 命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作
- Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- Invoker : 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
案例:
- 我们买了一套智能家电,又照明灯、风扇、冰箱、洗衣机,我们手机安装app就可以控制这些家电工作
- 这些智能家电来自不同的厂家,我们不想针对每一个厂家都安装一个APP,我们希望一个APP控制全部家电
- 要实现一个APP控制所有智能家电,则每个智能厂家都需要提供一个统一都接口给app调用,这时就可以考虑命令模式
- 命令模式可以将 “动作的请求” 者 从 “动作的执行者” 对象解析出来
实现:
- 创建 Command 接口
public interface Command {
/**
* 执行动作
*/
public void execute();
/**
* 撤销动作
*/
public void undo();
}
- 实现 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();
}
}
- 实现 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();
}
}
- 接受者 LightReceiver
public class LightReceiver {
public void on(){
System.out.println("电灯打开了");
}
public void off(){
System.out.println("电灯关闭了");
}
}
- 做一个空实现,用于初始化
public class Nocommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
- 执行者
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();
}
}
- 客户端
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/