《设计模式》命令模式
定义:
- 命令模式就是将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分离,消除请求发送者和请求接收者之间的耦合,让对象之间调用的关系更加灵活,同时命令模式也支持可撤销的操作。
命令模式的优缺点:
- 优点:降低系统的耦合度,将调用对象和实现对象解耦;扩展灵活,增加或删除命令不会影响其他类。并且容易设计一个命令队列,使用多线程去执行队列中的命令。
- 缺点:会导致系统有过多的具体命令类,增加系统的复杂度。
命令模式的使用场景:
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
- 系统需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
命令模式的角色组成:
抽象命令(Command):定义命令的接口,声明执行的方法。
具体命令(Concrete Command):具体的命令实现命令接口,通常会持有接收者,并调
用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver):接收者是真正执行命令的对象,任何类都可能成为一个接收
者,只要它能够实现命令要求实现的相应功能。
调用者/请求者(Invoker):调用命令对象执行请求,通常会持有命令对象,可以持有很
多的命令对象。
命令模式的原理类图如下所示:
案例背景:
生活中有这样一个场景:我们去饭店吃饭的时候,服务员先把菜单拿给我们,我们在选好菜之后,再将菜单交给服务员,然后由服务员将菜单告知后厨备菜。其实,在这样的场景中,也可以看成是命令模式的应用。服务员作为命令的调用者 Invoker,负责命令厨师备菜,厨师此时就是命令的接收者 Receiver,具体的命令就是顾客的订单 OrderCommand.
关系类图如下所示:
Command
接口:
public interface Command { void execute(); void undo(); }
OrderCommand
类:
public class OrderCommand implements Command{ private Chef receiver; private Order order; public OrderCommand(Chef receiver, Order order) { this.receiver = receiver; this.order = order; } @Override public void execute() { System.out.println("-------------------------"); System.out.println(order.getDiningTable() + "桌的订单:"); Set<String> keys = order.getFoodDic().keySet(); for (String key : keys) { receiver.makeFood(order.getFoodDic().get(key), key); } try { // 停顿一下模拟做饭的过程 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } System.out.println(order.getDiningTable() + "桌的饭弄好了"); } @Override public void undo() { System.out.println("--------------------------"); StringBuilder cancel = new StringBuilder(); cancel.append(order.getDiningTable()).append("桌的订单:"); Set<String> keys = order.getFoodDic().keySet(); for (String key : keys) { cancel.append(order.getFoodDic().get(key)).append("份").append(key); } receiver.cancelMakeFood(cancel.toString()); } }
Order
类:
public class Order { private int diningTable; private Map<String, Integer> foodDic = new HashMap<>(); public int getDiningTable() { return diningTable; } public void setDiningTable(int diningTable) { this.diningTable = diningTable; } public Map<String, Integer> getFoodDic() { return foodDic; } public void setFoodDic(String name, Integer num) { foodDic.put(name, num); } }
Chef
类:
public class Chef { public void makeFood(int num, String foodName) { System.out.println(num + "份" + foodName); } public void cancelMakeFood(String orderName) { System.out.println("取消" + orderName); } }
Waitor
类:
public class Waitor { private List<Command> commands; private List<Command> undoCommands; public Waitor() { commands = new ArrayList<>(); undoCommands = new ArrayList<>(); } public void setCommand(Command cmd){ commands.add(cmd); } public void setUndoCommands(Command cmd) { undoCommands.add(cmd); } public void orderUp() { System.out.println("服务员:叮咚,大厨,新订单来了......."); for (int i = 0; i < commands.size(); i++) { Command cmd = commands.get(i); if (cmd != null) { cmd.execute(); } } } public void cancelOrder() { for (int i = 0; i < undoCommands.size(); i++) { Command undoCmd = undoCommands.get(i); if (undoCmd != null) { undoCmd.undo(); } } } }
Client
类:
public class Client { public static void main(String[] args) { // 创建两个订单 Order order1 = new Order(); order1.setDiningTable(1); order1.setFoodDic("西红柿鸡蛋面", 1); order1.setFoodDic("小杯可乐", 1); Order order2 = new Order(); order2.setDiningTable(2); order2.setFoodDic("红烧排骨", 2); order2.setFoodDic("柠檬水", 2); // 创建接收者 Chef receiver = new Chef(); // 将订单和接收者封装成命令对象 OrderCommand cmd1 = new OrderCommand(receiver, order1); OrderCommand cmd2 = new OrderCommand(receiver, order2); // 创建调用者 waitor Waitor invoker = new Waitor(); invoker.setCommand(cmd1); invoker.setCommand(cmd2); // 撤销订单的命令 invoker.setUndoCommands(cmd2); invoker.orderUp(); invoker.cancelOrder(); } }
命令模式在 Spring 框架中的 JdbcTemplate 源码中的应用:
query 方法中的返回值类型 StatementCallback 就是一个命令接口 Command.
内部匿名类 QueryStatementCallback 就是一个具体的命令类同时也是命令接收者,实现了 doInStatement 方法
命令调用者就是 JdbcTemplate 类,在 execute 方法中调用了命令接口的 doInStatement 方法
StatementCallback
接口:
public interface StatementCallback<T> { @Nullable T doInStatement(Statement var1) throws SQLException, DataAccessException; }
JdbcTemplate
类:
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations { // ... @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())); } @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; } }