人物:大鸟,小菜
事件:小菜和大鸟去吃烧烤,发现路边的烧烤摊人多拥挤,老板在烤肉的同时可能记不住点菜人的名字,导致给错,没收钱,没烤熟等一系列问题,而且白天有城管,晚上太晚人们也不愿意吃路边摊,这些都导致了路边烧烤摊赚钱不稳定,而有店面的烧烤店,虽然要交一定的租金,但是没了哪些不稳定因素,收入就相对稳定了,大鸟就烧烤店好过烧烤摊这个情景,引出了命令模式。
命令模式:
1.借烤肉事件设计代码,初次设计为紧耦合设计
2.在明确紧耦合设计的不好点后,又进行了松耦合设计
3.进一步完善松耦合设计
4.最后借上述例子引出命令模式并阐述了优点
紧耦合设计
趁着烤串还没出来,大鸟让小菜设计实现烧烤摊营业模式
Barbecuer类,路边烤肉实现:
@Slf4j public class Barbecuer { public void BakeMutton() { log.info("烤羊肉串"); } public void BakeChickenWing() { log.info("烤鸡翅!"); } }
客户端:
public class BarbecueClient { public static void main(String[] args) { Barbecuer boy = new Barbecuer(); boy.BakeMutton(); boy.BakeMutton(); boy.BakeMutton(); boy.BakeChickenWing(); boy.BakeMutton(); boy.BakeMutton(); boy.BakeChickenWing(); } }
大鸟:这样人一多,请求一多久容易乱,你再试试烧烤店营业模式
松耦合设计
代码结构图:
Command类,即抽象命令类:
public abstract class Command { protected Barbecuer receiver; public Command(Barbecuer receiver) { this.receiver = receiver; } abstract public void excuteCommand(); }
烤羊肉串,即具体命令类:
public class BakeMuttonCommand extends Command { public BakeMuttonCommand(Barbecuer receiver) { super(receiver); } @Override public void excuteCommand() { receiver.BakeMutton(); } }
烤鸡翅,即具体命令类:
public class BakeChickenWingCommand extends Command { public BakeChickenWingCommand(Barbecuer receiver) { super(receiver); } @Override public void excuteCommand() { receiver.BakeChickenWing(); } }
Waiter类,即服务员类:
public class Waiter { private Command command; public void setCommand(Command command) { this.command = command; } public void notifyCook() { command.excuteCommand(); } }
客户端代码:
public class BarbecueClient { public static void main(String[] args) { //开店前的准备 Barbecuer boy = new Barbecuer(); Command bakeMuttonCommand1 = new BakeMuttonCommand(boy); Command bakeMuttonCommand2 = new BakeMuttonCommand(boy); Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy); Waiter girl = new Waiter(); //开门营业 girl.setCommand(bakeMuttonCommand1); girl.notifyCook(); girl.setCommand(bakeMuttonCommand2); girl.notifyCook(); girl.setCommand(bakeChickenWingCommand1); girl.notifyCook(); } }
大鸟:不错,功能基本实现了,但是和实际场景有点不符合,因为一般叫烤串是一次叫6串,而不是一串一串叫叫6次,还有就是客户也不知道到底有没有卖完,最后就是客户点了哪些食物饮料,都是需要记录的,不然人一多,就忘了。
松耦合的进一步优化
小菜进行了第三版编写:
服务员类:
@Slf4j public class Waiter { private List<Command> orders = new ArrayList<>(); /** * 设置订单 * * @param command */ public void setOrder(Command command) { if (command.getClass().getName().equalsIgnoreCase("com.example.pmingup.service.designModeWan.BakeChickenWingCommand")) { log.info("服务员,鸡翅没有了,请点别的烧烤"); } else { orders.add(command); log.info("增加订单:" + command.getClass().getName() + "事件: " + new Date()); } } /** * 取消订单 * * @param command */ public void cancelOrder(Command command) { orders.remove(command); log.info("取消订单:" + command.toString() + "事件:" + new Date()); } /** * 通知全部执行 */ public void notifyCook() { for (Command cd : orders) { cd.excuteCommand(); } } }
客户端代码:
public class BarbecueClient { public static void main(String[] args) { //开店前的准备 Barbecuer boy = new Barbecuer(); Command bakeMuttonCommand1 = new BakeMuttonCommand(boy); Command bakeMuttonCommand2 = new BakeMuttonCommand(boy); Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy); Waiter girl = new Waiter(); //开门营业 girl.setOrder(bakeMuttonCommand1); girl.setOrder(bakeMuttonCommand2); girl.setOrder(bakeChickenWingCommand1); girl.notifyCook(); } }
命令模式
1.概念:将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
2.结构图:
3.代码如下:
CommandDemo类,来声明执行操作的接口:
public abstract class CommandDemo { protected Receiver reciever; public CommandDemo(Receiver reciever) { this.reciever = reciever; } abstract public void execute(); }
ConcreteCommand类,将一个接受者对象绑定一个动作,调用接受者相应的操作,以实现execute():
public class ConcreteCommand extends CommandDemo { public ConcreteCommand(Receiver reciever) { super(reciever); } @Override public void execute() { reciever.action(); } }
Invoker类,要求该命令执行这个请求:
public class Invoker { private CommandDemo commandDemo; public void setCommandDemo(CommandDemo commandDemo) { this.commandDemo = commandDemo; } public void executeCommand() { commandDemo.execute(); } }
Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接受者:
@Slf4j public class Receiver { public void action() { log.info("执行请求!"); } }
客户端代码:
public class BarbecueClient { public static void main(String[] args) { Receiver r = new Receiver(); CommandDemo c = new ConcreteCommand(r); Invoker i = new Invoker(); i.setCommandDemo(c); i.executeCommand(); } }
4.优点:
(1)能较容易地设计一个命令队列
(2)需要时,能较容易地将命令记入日志
(3)允许接收请求的一方决定是否要求否决请求
(4)容易实现对请求的撤销和重做
(5)新加的命令不会影响其他类,因此增加新的命令很容易
(6)命令模式把请求一个操作的对象与知道怎么执行操作的一个对象分割开来