在说命令模式前咱们先来讲一个小例子。不少人都有吃夜市的经历,对于那些推小车的摊位,一般只有老板一我的,既负责制做也负责收钱,我要两串烤串多放辣,旁边的人要了三串烤面筋不要辣,过了一下子又来人要烤蔬菜……,当人多的时候记忆力很差的老板确定就不知道谁要的啥、交没交钱了;而去有店铺的烤肉摊,点单的时候会有服务员来记录咱们的菜单,而后再去通知烧烤师傅进行烧烤,这样就不会出现混乱了,固然咱们也能够随时对菜单进行修改,此时只需服务员记录后去通知烤肉师傅便可,因为有了记录,最终算帐仍是不会出错的。git
从这里讲,前者其实就是“行为请求者”和“行为实现者”的紧耦合,对于请求排队或记录请求日志,以及支持可撤销的操做来讲,紧耦合是不太合适的,而命令模式偏偏解决了这点问题。ide
命令模式(Command),将一个请求封装为一个对象,从而使你可用不一样的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操做。UML结构图以下:this
其中,Invoker是调用者角色,要求该命令执行这个请求;Command是命令角色,须要执行的全部命令都在这里声明,能够是接口或抽象类;Receiver是接收者角色,知道如何实施与执行一个请求相关的操做,任何类均可能做为一个接收者;ConcreteCommand将一个接收者对象绑定与一个动做,调用接收者相应的操做,以实现Execute。spa
用来声明执行操做的接口/抽象类。日志
1 public abstract class Command { 2 3 protected Receiver receiver; 4 5 public Command(Receiver receiver) { 6 this.receiver = receiver; 7 } 8 9 //执行命令的方法 10 abstract public void execute(); 11 12 }
具体的Command类,用于构造传递接收者,根据环境需求,具体的命令类也可能有n个。code
1 public class ConcreteCommand extends Command { 2 3 //构造传递接收者 4 public ConcreteCommand(Receiver receiver) { 5 super(receiver); 6 } 7 8 //必须实现一个命令 9 @Override 10 public void execute() { 11 receiver.action(); 12 } 13 14 }
接收命令,并执行命令。对象
1 public class Invoker { 2 3 private Command command; 4 5 //接受命令 6 public void setCommand(Command command) { 7 this.command = command; 8 } 9 10 //执行命令 11 public void executeCommand() { 12 command.execute(); 13 } 14 15 }
该角色就是干活的角色, 命令传递到这里是应该被执行的。blog
1 public class Receiver { 2 3 public void action() { 4 System.out.println("执行请求!"); 5 } 6 7 }
首先定义一个接收者,而后定义一个命令用于发送给接收者,以后再声明一个调用者,便可把命令交给调用者执行。接口
1 public class Client { 2 3 public static void main(String[] args) { 4 //定义接收者 5 Receiver receiver = new Receiver(); 6 //定义一个发送给接收者的命令 7 Command command = new ConcreteCommand(receiver); 8 //声明调用者 9 Invoker invoker = new Invoker(); 10 11 //把命令交给调用者执行 12 invoker.setCommand(command); 13 invoker.executeCommand(); 14 } 15 16 }
运行结果以下:事务
下面以上面提到的烧烤店模型为例,使用命令模式编写代码实现。类图以下:
服务员类为调用者角色,在其中定义一个订单列表用于存储客户订单信息,经过setOrder()方法设置订单、cancelOrder()方法取消订单、notifyExecute()方法下单。
1 public class Waiter { 2 3 private List<Command> orders = new LinkedList<>(); 4 5 //设置订单 6 public void setOrder(Command command) throws Exception { 7 //经过反射得到鸡翅的类 8 String s1 = Class.forName("com.adamjwh.gofex.command.BakeChickenWingCommand").toString().substring(6); 9 //获取command订单中的类 10 String s2 = command.toString().substring(0, command.toString().indexOf("@")); 11 12 //这里模拟鸡翅卖完的状况,当订单中有鸡翅时,撤销订单 13 if(s1.equals(s2)) { 14 System.out.println("【服务员:鸡翅没有了,请点别的烧烤】"); 15 cancelOrder(command);//撤销订单 16 } else { 17 orders.add(command); 18 System.out.println("添加订单:" + command.getBarbecuer() + "\t时间:" + new Date().toString()); 19 } 20 } 21 22 //取消订单 23 public void cancelOrder(Command command) { 24 orders.remove(command); 25 System.out.println("取消订单:" + command.getBarbecuer() + "\t时间:" + new Date().toString()); 26 } 27 28 //通知所有执行 29 public void notifyExecute() { 30 System.out.println("-----------------------订单-----------------------"); 31 for(Command command : orders) { 32 command.excuteCommand(); 33 } 34 } 35 }
1 public abstract class Command { 2 3 protected Barbecuer receiver; 4 5 public Command(Barbecuer receiver) { 6 this.receiver = receiver; 7 } 8 9 //执行命令 10 abstract public void excuteCommand(); 11 12 //获取名称 13 abstract public String getBarbecuer(); 14 15 }
这里的接收者角色就是烧烤师傅,提供“烤羊肉串”和“烤鸡翅”的操做。
1 public class Barbecuer { 2 3 //烤羊肉 4 public void bakeMutton() { 5 System.out.println("烤羊肉串"); 6 } 7 8 //烤鸡翅 9 public void bakeChickenWing() { 10 System.out.println("烤鸡翅"); 11 } 12 13 }
这里以烤羊肉串类为例,提供了执行命令的方法。烤鸡翅类同理,此处再也不赘述。
1 public class BakeMuttonCommand extends Command { 2 3 private String barbecuer; 4 5 public BakeMuttonCommand(Barbecuer receiver) { 6 super(receiver); 7 barbecuer = "烤羊肉串"; 8 } 9 10 @Override 11 public void excuteCommand() { 12 receiver.bakeMutton(); 13 } 14 15 //获取名称 16 public String getBarbecuer() { 17 return barbecuer; 18 } 19 20 }
开店前准备即初始化烤肉师傅、服务员及命令类,顾客点菜后将菜单信息存入服务员的订单上,假设鸡翅卖完了(参考Waiter类),则将鸡翅项从订单上删除(即“撤销”),而后使用notifyExecute()方法通知烤肉师傅。
1 public class Client { 2 3 public static void main(String[] args) throws Exception { 4 //开店前准备 5 Barbecuer barbecuer = new Barbecuer(); 6 Command bakeMuttonCommand1 = new BakeMuttonCommand(barbecuer); 7 Command bakeMuttonCommand2 = new BakeMuttonCommand(barbecuer); 8 Command bakeChickenWingCommand1 = new BakeChickenWingCommand(barbecuer); 9 Waiter waiter = new Waiter(); 10 11 //开门营业,顾客点菜 12 waiter.setOrder(bakeMuttonCommand1); 13 waiter.setOrder(bakeMuttonCommand2); 14 //这里假设鸡翅卖完了 15 waiter.setOrder(bakeChickenWingCommand1); 16 17 //点菜完毕,通知厨房 18 waiter.notifyExecute(); 19 } 20 21 }
运行结果以下:
命令模式实际上是把一个操做的对象与知道怎么执行一个操做的对象分隔开。至于命令模式使用时机,敏捷开发原则告诉咱们,不要为代码添加基于猜想的、实际不须要的功能。若是不清楚一个系统是否须要命令模式,通常就不要着急去实现它,事实上,在须要的时候经过重构实现这个模式并不困难,只有在真正须要如撤销/恢复操做等功能时,把原来的代码重构为命令模式才有意义。