介绍
命令模式是一种行为型设计模式。在命令模式中,所有的请求都会被包装成为一个对象。
参考了一下其他关于命令模式的文章,其中有谈到说是可以用不同的请求对客户进行参数化。对这句话的理解是,因为将请求封装成为对象,所以客户的所有操作,其实就是多个命令类的对象而已,即参数化了。
命令模式的最大的特点就是将请求的调用者与请求的最终执行者进行了解耦。调用者需要关心的仅仅是请求对象是否被执行了,对于请求对象是如何执行的,对什么进行操作的,统统不需要关心。
**原理:**命令模式中,一般有如下几个角色:
- command:命令的抽象接口,其中包含execute方法。根据业务需求,有的还会包含其他通用方法如undo等。
- concreteCommand:具体的命令实现类。每一种请求,都会映射一个具体的命令实现类。对于每个类,都会实现execute方法,并且依赖receiver,也就是接收者对象。execute方法中,一般就是调用接收者对象的对应方法,从而实现对请求的最终处理。
- receiver:请求的接收者,也是请求的最终的执行者,被命令实现类所依赖。
- invoker:请求的调用者。调用者会调用所有传入的命令对象的execute方法,开启命令的执行,但是不会与最终的执行者receive耦合,两者中间是通过命令实现类进行联系和沟通的。
- client:进行接收者对象和命令对象的创建,并建立两者之间的联系。
适用场景:涉及到“命令”、“操作”或者“控制”的场景,一般都是命令模式的适用场景。
- 餐厅点菜的过程,消费者(client)说要吃某几种菜(命令对象),赶快做好端上来。服务员(invoker)会记录所有点过的菜品(保存所有的命令对象),然后将订单给后厨说,按照单子做(调用所有命令对象的execute)。之后就会启动每一道菜品的制作流程。对于菜品如何烹制,与服务员是没有关系的,两者不耦合。
- 遥控器的运行过程也可以理解成是一种命令模式的应用。假设有一个智能家居的遥控器,在面板上,可以控制电灯的开关,空调的开关(各种命令对象)。遥控器就是invoker的角色,负责实际命令的调用。而最终命令的执行,则是各种电器(receiver)来进行的。
案例
背景:我们以顾客点菜为例。顾客有一个菜单(invoker),能够预定甜点、牛奶、水果,取消甜点、牛奶、水果等操作。
实现:
命令接口:
1/** 2 * @program: test 3 * @description: 命令 4 * @author: xingcheng 5 * @create: 2018-09-02 15:16 6 **/ 7public interface Command { 8 9 /** 10 * 执行命令 11 */ 12 void execute(); 13 14}
定义receiver:
1/** 2 * @program: test 3 * @description: 水果 4 * @author: xingcheng 5 * @create: 2018-09-02 15:18 6 **/ 7public class Fruit { 8 9 /** 10 * 点一份水果 11 */ 12 void orderFruit(){ 13 System.out.println("来一份水果"); 14 } 15 16 /** 17 * 取消水果 18 */ 19 void cancelFruit(){ 20 System.out.println("取消水果"); 21 } 22} 23 24/** 25 * @program: test 26 * @description: 牛奶 27 * @author: xingcheng 28 * @create: 2018-09-02 15:22 29 **/ 30public class Milk { 31 32 /** 33 * 点一份牛奶 34 */ 35 void orderMilk(){ 36 System.out.println("来一份牛奶"); 37 } 38 39 /** 40 * 取消牛奶 41 */ 42 void cancelMilk(){ 43 System.out.println("取消牛奶"); 44 } 45} 46 47/** 48 * @program: test 49 * @description: 甜点 50 * @author: xingcheng 51 * @create: 2018-09-02 15:17 52 **/ 53public class Mousse { 54 55 /** 56 * 点一份甜点 57 */ 58 void orderMousse(){ 59 System.out.println("来一份甜点"); 60 } 61 62 /** 63 * 取消甜点 64 */ 65 void cancelMousse(){ 66 System.out.println("取消甜点"); 67 } 68}
定义具体命令:
1/** 2 * @program: test 3 * @description: 点一份水果 4 * @author: xingcheng 5 * @create: 2018-09-02 15:29 6 **/ 7public class OrderFruit implements Command{ 8 9 private Fruit fruit; 10 11 public OrderFruit(Fruit fruit) { 12 this.fruit = fruit; 13 } 14 15 /** 16 * 执行命令 17 */ 18 @Override 19 public void execute() { 20 fruit.orderFruit(); 21 } 22} 23 24/** 25 * @program: test 26 * @description: 点一份牛奶 27 * @author: xingcheng 28 * @create: 2018-09-02 15:25 29 **/ 30public class OrderMilk implements Command{ 31 32 private Milk milk; 33 34 public OrderMilk(Milk milk) { 35 this.milk = milk; 36 } 37 38 /** 39 * 执行命令 40 */ 41 @Override 42 public void execute() { 43 milk.orderMilk(); 44 } 45} 46 47/** 48 * @program: test 49 * @description: 点一份甜点 50 * @author: xingcheng 51 * @create: 2018-09-02 15:31 52 **/ 53public class OrderMousse implements Command{ 54 55 private Mousse mousse; 56 57 public OrderMousse(Mousse mousse) { 58 this.mousse = mousse; 59 } 60 61 /** 62 * 执行命令 63 */ 64 @Override 65 public void execute() { 66 mousse.orderMousse(); 67 } 68} 69 70/** 71 * @program: test 72 * @description: 取消水果 73 * @author: xingcheng 74 * @create: 2018-09-02 15:30 75 **/ 76public class CancelFruit implements Command { 77 78 private Fruit fruit; 79 80 public CancelFruit(Fruit fruit) { 81 this.fruit = fruit; 82 } 83 84 /** 85 * 执行命令 86 */ 87 @Override 88 public void execute() { 89 fruit.cancelFruit(); 90 } 91} 92 93/** 94 * @program: test 95 * @description: 取消牛奶 96 * @author: xingcheng 97 * @create: 2018-09-02 15:27 98 **/ 99public class CancelMilk implements Command{ 100 101 private Milk milk; 102 103 public CancelMilk(Milk milk) { 104 this.milk = milk; 105 } 106 107 /** 108 * 执行命令 109 */ 110 @Override 111 public void execute() { 112 milk.cancelMilk(); 113 } 114} 115 116/** 117 * @program: test 118 * @description: 取消甜点 119 * @author: xingcheng 120 * @create: 2018-09-02 15:32 121 **/ 122public class CancelMousse implements Command { 123 124 private Mousse mousse; 125 126 public CancelMousse(Mousse mousse) { 127 this.mousse = mousse; 128 } 129 130 /** 131 * 执行命令 132 */ 133 @Override 134 public void execute() { 135 mousse.cancelMousse(); 136 } 137}
定义菜单(invoker)
1/** 2 * @program: test 3 * @description: 菜单 4 * @author: xingcheng 5 * @create: 2018-09-02 15:36 6 **/ 7public class Menu { 8 9 private List<Command> commands; 10 11 public Menu() { 12 this.commands = new ArrayList<>(); 13 } 14 15 /** 16 * 记录顾客需要什么 17 */ 18 public void writeMenu(Command command){ 19 commands.add(command); 20 } 21 22 /** 23 * 将菜单交给厨师 24 */ 25 public void giveCooker(){ 26 if (commands != null && commands.size() > 0){ 27 commands.forEach(command -> command.execute()); 28 } 29 } 30}
顾客购买:
1/** 2 * @program: test 3 * @description: 顾客 4 * @author: xingcheng 5 * @create: 2018-09-02 15:34 6 **/ 7public class Consumer { 8 9 public static void main(String[] args) { 10 Fruit fruit = new Fruit(); 11 Milk milk = new Milk(); 12 Mousse mousse = new Mousse(); 13 14 // 店小二记录菜单 15 System.out.println("店小二记录菜单----------------------------------------------"); 16 Menu menu = new Menu(); 17 menu.writeMenu(new OrderFruit(fruit)); 18 menu.writeMenu(new OrderMilk(milk)); 19 menu.writeMenu(new OrderMousse(mousse)); 20 21 // 店小二将菜单交给厨师 22 menu.giveCooker(); 23 24 // 顾客取消菜单--太贵了不吃了╭(╯^╰)╮ 25 System.out.println("顾客取消菜单--太贵了不吃了╭(╯^╰)╮----------------------------"); 26 Menu menuCancel = new Menu(); 27 menuCancel.writeMenu(new CancelFruit(fruit)); 28 menuCancel.writeMenu(new CancelMilk(milk)); 29 menuCancel.writeMenu(new CancelMousse(mousse)); 30 menuCancel.giveCooker(); 31 32 System.out.println("宏命令----------------------------"); 33 List<Command> commands = new ArrayList<>(); 34 commands.add(new CancelFruit(fruit)); 35 commands.add(new CancelMilk(milk)); 36 commands.add(new CancelMousse(mousse)); 37 MutilCommand mutilCommand = new MutilCommand(commands); 38 mutilCommand.giveCooker(); 39 } 40 41}
结果:

延伸:
命令模式中有一种扩展,叫做宏命令,能同时进行一组命令的执行。比如遥控器只存在两个按键,一个控制所有电器的开启,一个控制所有电器的关闭。那么我们不需要改动已有的代码,只要扩展一个组合命令类,其中包含多个命令即可。
1/** 2 * @program: test 3 * @description: 宏命令(组合命令) 4 * @author: xingcheng 5 * @create: 2018-09-02 15:59 6 **/ 7public class MutilCommand { 8 9 private List<Command> commands; 10 11 public MutilCommand(List<Command> commands) { 12 this.commands = commands; 13 } 14 15 /** 16 * 记录顾客需要什么 17 */ 18 public void writeMenu(Command command){ 19 commands.add(command); 20 } 21 22 /** 23 * 将菜单交给厨师 24 */ 25 public void giveCooker(){ 26 if (commands != null && commands.size() > 0){ 27 commands.forEach(command -> command.execute()); 28 } 29 } 30 31}
总结:
命令模式的核心思想就是将命令或者请求封装成对象,分离请求调用者和请求最终执行者。
优点:将请求调用者和执行者解耦,适用于底层接口封装,可以通过只增加类就可以实现接口扩展,不需要修改原来的代码。
缺点:如果存在较多的命令或者请求,需要较多的命令类。