0.参考资料
1.概述
- 将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。 —《设计模式》GoF
- 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
1.1动机
- 在软件构建过程中,“行为请求者” 与“行为实现者”通常呈现一种 "紧耦合"。但在某些场合 -- 比如需要对行为进行"记录、撤销/重做(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。- 在这种情况下,如何将"行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
1.2结构
- - - 对原理类图的说明-即(命名模式的角色及职责)- 1) Invoker 是调用者角色- 2) Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类- 3) Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作- 4) ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute
2.要点总结
宏观架构
1. Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦, 在面向对象语言中,常见的实现手段是”将行为抽象为对象”1. 实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。 通过使用Composite模式 ,可以将多个”命令”封装为一个个"复合命令" MacroCommand.1. Command模式与C+ +中的函数对象有些类似。但两者定义行为接口的规范有所区别: Command以面向对象中的"接口-实现” 来定义行为接口规范,更严格,但有性能损失; C+ +函数对象以函数签名来定义行为接口规范,更灵活,性能更高。
微观代码
1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要
调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对 象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:” 请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到 了纽带桥梁的作用。
1. 可以设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令1. 方便实现对请求的撤销和重做1. **命令模式不足:**可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这
点在在使用的时候要注意
1. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没
有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
1. 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令) 订单的撤销/恢复、触发-反馈机制
3.案例
需求
1. 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。1. 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个app就可以控制全部智能家电。1. 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用
分析
4.使用模式
方案
- 
类图
- - 
代码
- Command
// 命令接口public interface Command {// 执行命令void execute();// 撤销命令void undo();}// part1 的 on 命令public class ConcreteCommand_part1_on implements Command {Part1 part1;public ConcreteCommand_part1_on(Part1 part1){this.part1 = part1;}@Overridepublic void execute() {part1.on();}@Overridepublic void undo() {part1.off();}}// part1 的 off 命令public class ConcreteCommand_part1_off implements Command {Part1 part1;public ConcreteCommand_part1_off(Part1 part1){this.part1 = part1;}@Overridepublic void execute() {part1.off();}@Overridepublic void undo() {part1.on();}}// part2....public class ConcreteCommand_part2_on implements Command {Part2 part2;public ConcreteCommand_part2_on(Part2 part2){this.part2 = part2;}@Overridepublic void execute() {part2.on();}@Overridepublic void undo() {part2.off();}}public class ConcreteCommand_part2_off implements Command {Part2 part2;public ConcreteCommand_part2_off(Part2 part2){this.part2 = part2;}@Overridepublic void execute() {part2.off();}@Overridepublic void undo() {part2.on();}}
- 接收者
// 可忽略的开关接口public interface Openable {void on();void off();}public class Part1 implements Openable {@Overridepublic void on() {System.out.println("part1 开");}@Overridepublic void off() {System.out.println("part1 关");}}public class Part2 implements Openable {@Overridepublic void on() {System.out.println("part2 开");}@Overridepublic void off() {System.out.println("part2 关");}}
- 调用者
public class MyInvoke {// 两条集合管理所有开关按钮List<Command> onCommands = new ArrayList<>(10);List<Command> offCommands = new ArrayList<>(10);// 记录的可撤销一次Command undoCommand;// 添加组件public void addCommand(int index, Command onCommand, Command offCommand){onCommands.add(index, onCommand);offCommands.add(index, offCommand);}// 按下某组件的开按钮,public void onButtonForIndex(int index){onCommands.get(index).execute();// 记录操作, 以撤销undoCommand = onCommands.get(index);}// 按下某组件的关按钮,public void offButtonForIndex(int index){offCommands.get(index).execute();// 记录操作, 以撤销undoCommand = offCommands.get(index);}// 撤销操作public void undo(){undoCommand.undo();}}
- 测试
public class Client {public static void main(String[] args) {// 初始化一个(空)遥控器MyInvoke invoke = new MyInvoke();// 创建接收者一号: part1, 二号Part1 part1 = new Part1();Part2 part2 = new Part2();// 创建接收者对应的开关命令Command part1_on_cmd = new ConcreteCommand_part1_on(part1);Command part1_off_cmd = new ConcreteCommand_part1_off(part1);Command part2_on_cmd = new ConcreteCommand_part2_on(part2);Command part2_off_cmd = new ConcreteCommand_part2_off(part2);// 开关命令填充进遥控器invoke.addCommand(0, part1_on_cmd, part1_off_cmd);invoke.addCommand(1, part2_on_cmd, part2_off_cmd);// 使用测试System.out.println("\t按下 part1 的 on:");invoke.onButtonForIndex(0);System.out.println("\t按下 part1 的 off:");invoke.offButtonForIndex(0);System.out.println("\t 撤回:...");invoke.undo();System.out.println("\t按下 part2 的 on:");invoke.onButtonForIndex(1);System.out.println("\t按下 part2 的 off:");invoke.offButtonForIndex(1);System.out.println("\t 撤回:...");invoke.undo();}}
按下 part1 的 on:part1 开按下 part1 的 off:part1 关撤回:...part1 开按下 part2 的 on:part2 开按下 part2 的 off:part2 关撤回:...part2 开
5.经典使用
5.1Spring中JdbcTemplate
说明
- 
分析
- StatementCallback 接口, 命令接口(Command). 其中 doInStatement(...) 即命令方法- class QueryStatementCallback implements StatementCallback, SqlProvider , 匿名内<br />部类, 实现了命令接口. 调用真正的执行方法,真正的执行方法即stmt的executeQuery()。- JdbcTemplate即充当调用者角色,又充当接收者角色。- execute(StatementCallback action) ,调用者的调用方法. 调用了 (具体的的)action.doInStatement 方法.
