1 定义
将一个请求封装为一个对象,从而可以使用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销操作。
2 优势
- 能较容易的设计一个命令队列。
- 在需要的情况下,可以较容易的将命令记入日志。
- 允许接受请求的一方决定是否要否决请求。
- 可以容易的实现对请求的撤销和重做。
- 由于加进具体命令类不影响其他的类,因此增加新的具体命令操作很容易。
- 最重要一点就是:命令模式把请求一个操作的对象和怎么执行一个操作的对象解耦。
3 UML图
4 例子
4.1 场景
饭店点餐问题。客人点餐是请求一个操作;客人修改点的菜单是对已有操作的修改;后厨根据客人下的单做菜,是执行一个操作;但客人并不需要和后厨直接打交道,而是通过服务员完成点餐、修改菜,在没有食材的情况下否决客人的请求操作。
4.3 code
Main
public class Main {
public static void main(String[] args) {
Waiter waiter=new Waiter();
Barbecuer cook=new Barbecuer();
kaoYangRouCommand kaoYangRouCommand1=new kaoYangRouCommand(cook);
kaoYangRouCommand kaoYangRouCommand2=new kaoYangRouCommand(cook);
kaoChiCommand kaoChiCommand=new kaoChiCommand(cook);
waiter.setOrder(kaoYangRouCommand1);
waiter.setOrder(kaoYangRouCommand2);
waiter.setOrder(kaoChiCommand);
waiter.cancelOrder(kaoYangRouCommand2);
//通知厨师
waiter.notifyCook();
}
}
AbstractCommand
public abstract class AbstractCommand {
protected Barbecuer receiver;
public AbstractCommand(Barbecuer receiver){
this.receiver=receiver;
}
abstract public void excute();
}
kaoYangRouCommand
public class kaoYangRouCommand extends AbstractCommand {
public kaoYangRouCommand(Barbecuer receiver) {
super(receiver);
}
@Override
public void excute() {
receiver.kaoYangRou();
}
}
kaoChiCommand
public class kaoChiCommand extends AbstractCommand {
public kaoChiCommand(Barbecuer receiver) {
super(receiver);
}
@Override
public void excute() {
receiver.kaoChi();
}
}
Barbecuer
public class Barbecuer {
public void kaoYangRou(){
System.out.println("烤羊肉串");
}
public void kaoChi(){
System.out.println("烤鸡翅");
}
}
Waiter
public class Waiter {
//用一个list保存很多请求的引用
List<AbstractCommand> list = new ArrayList<>();
//接受请求
public void setOrder(AbstractCommand command) {
String str=command.getClass().getSimpleName();
if(str.equals("kaoChiCommand")){
System.out.println("没有烤翅了,请点别的烧烤");
}else{
list.add(command);
System.out.println("增加订单,"+str+"时间:"+new Date().toString());
}
}
//撤销请求
public void cancelOrder(AbstractCommand command) {
list.remove(command);
String str=command.getClass().getSimpleName();
System.out.println("取消订单,"+str+"时间:"+new Date().toString());
}
//通知后厨做菜
public void notifyCook() {
list.forEach(command -> command.excute());
}
}
5 注意
敏捷开发原则告诉我们:不要为代码添加基于猜测的、实际不需要的功能。如果不清楚是否要用命令模式,一般就不要实现。在需要的时候通过重构再去实现这个模式,只有在真正需要如“撤销/恢复”操作等功能时,把原来的代码重构为命令模式才有意义。