设计模式----命令模式
外面饭馆大致有这样的形式:路边摊和店面餐馆。
不难发现如果路边摊生意火热的话可能有很多顾客围在厨师身边,这样很容易出现错误,因为耦合度太高了,厨师和每位顾客都耦合在一起。
而店面则会有服务员,服务员负责记录顾客想要点的菜品,如果顾客想修改的话只要在菜单上划掉就可以了,出错率很小。
这也就对应了一个设计模式:命令模式。
UML类图:
代码:
知道功能如何实现类:
public class Receiver { public void action(){ System.out.println("执行请求!"); } }
功能接口类:
public abstract class Command { protected Receiver receiver; public Command(Receiver receiver){ this.receiver = receiver; } public abstract void excute(); }
具体功能类:
public class ConcreteCommand extends Command{ public ConcreteCommand(Receiver receiver) { super(receiver); } @Override public void excute() { receiver.action(); } }
功能实现类:
public class Invoke { private Command command; public void setCommand(Command command){ this.command = command; } public void executeCommand(){ this.command.excute(); } }
客户端:
public class Client { public static void main(String[] args) { Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoke invoke = new Invoke(); invoke.setCommand(command); invoke.executeCommand(); } }
客户端代码:
执行请求!
回到我们刚刚的例子:在餐馆点餐,用这个模式该如何实现呢,除此之外还要求有删除菜品和日志功能。
厨师类:
public class Cooker { public void cookMutton(){ System.out.println("烤羊肉串"); } public void cookMeat(){ System.out.println("做红烧肉"); } }
抽象菜品类:
public abstract class Dish { protected Cooker cooker; public Dish(Cooker cooker){ this.cooker = cooker; } public abstract void execute(); }
红烧肉类:
public class Meet extends Dish{ public Meet(Cooker cooker) { super(cooker); } @Override public void execute() { cooker.cookMeat(); } }
烤羊肉串类:
public class Mutton extends Dish{ public Mutton(Cooker cooker) { super(cooker); } @Override public void execute() { cooker.cookMutton(); } }
服务员类:
public class Waiter { private List<Dish> dishes = new ArrayList<>(); public void addDish(Dish dish){ if(dish instanceof Meet){ System.out.println("抱歉,红烧肉做完了,请点羊肉串吧。"); return; } dishes.add(dish); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("增加订单 : " + dish.getClass().getName() + ",当前时间为:" + sdf.format(new Date()) ); } public void cancerDish(Dish dish){ dishes.remove(dish); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("取消订单 : " + dish.getClass().getName() + ",当前时间为:" + sdf.format(new Date())); } public void notifyAllDo(){ for (Dish dish : dishes) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.print("执行订单 : " + dish.getClass().getName() + ",当前时间为:" + sdf.format(new Date()) + " "); dish.execute(); } } }
客户端:
public static void main(String[] args) { Cooker cooker = new Cooker(); Meet meet = new Meet(cooker); Mutton mutton = new Mutton(cooker); Waiter waiter = new Waiter(); waiter.addDish(meet); for(int i = 0;i < 10;i++){ waiter.addDish(mutton); } for (int i = 0;i < 4;i++){ waiter.cancerDish(mutton); } waiter.notifyAllDo(); }
输出:
抱歉,红烧肉做完了,请点羊肉串吧。 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 增加订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 取消订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 取消订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 取消订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 取消订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串 执行订单 : example.Mutton,当前时间为:2021-09-25 20:33:40 烤羊肉串
总结:
/** * @author 陈柏宇 * 将一个请求封装封装成为一个对象从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作 * * 命令模式的优点: * 1、容易设计一个命令队列 * 2、在需要的情况下,可以较容易的将命令记入日志 * 3、允许接收请求的一方决定是否要否决请求 * 4、可以容易地实现对请求的撤销与重做, * 5、由于加进新的具体命令类不影响其他类,因此增加新的具体命令类很容易 * 6、把请求一个操作的对象和知道怎么执行一个操作的对象分割开来。 * * 根据敏捷开发原则,只有真正需要撤销、恢复功能时,把原来的代码重构为命令模式才有意义。 * */