命令模式
命令模式定义:在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。
在网上找了个命令模式的标准类图
命令模式中有五种角色,分别是:
1、抽象命令(Command):定义命令的接口,声明执行的方法
2、具体命令(ConcreteCommand):具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作。
3、接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能。
4、调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
5、客户端(Client):命令由客户端来创建,并设置命令的接收者。
我们以顾客到餐馆吃饭为例,顾客先要点单,然后交与服务员,服务员拿着单子去找后厨按要求做菜,这便是命令模式的体现,实现了顾客和厨师的解耦,在这个过程中顾客便是我们的client,单子便是具体的command,服务员是invoker,厨师就是具体的实现者receiver。下面是demo
首先是makefood接口,相当于抽象命令
package io.powerx.command; public interface MakeFood { void execute(); }
三种实现类,制作不同的食物,在实现类中持有我们的cook,调用cook的方法完成制作
package io.powerx.command; public class MakeBread implements MakeFood { private Cook cook; public MakeBread(Cook cook) { super(); this.cook = cook; } @Override public void execute() { cook.makeBread(); } }
package io.powerx.command; public class MakeRoast implements MakeFood { private Cook cook; public MakeRoast(Cook cook) { super(); this.cook = cook; } @Override public void execute() { cook.makeRoast(); } }
package io.powerx.command; public class MakeChicken implements MakeFood { private Cook cook; public MakeChicken(Cook cook) { super(); this.cook = cook; } @Override public void execute() { cook.makeChicken(); } }
厨师类
package io.powerx.command; public class Cook { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Cook(String name) { super(); this.name = name; } public void makeBread() { System.out.println(name +" make bread"); } public void makeRoast() { System.out.println(name + " make roast"); } public void makeChicken () { System.out.println(name + " make chicken"); } }
服务员类,它持有一个Makefood的list,用来存储顾客的点单信息
package io.powerx.command; import java.util.ArrayList; import java.util.List; public class Waiter { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<MakeFood> getList() { return list; } public void setList(List<MakeFood> list) { this.list = list; } public Waiter(String name) { super(); this.name = name; this.list = new ArrayList<>(); } private List<MakeFood> list; public void receive(MakeFood makeFood) { list.add(makeFood); } public void assign() { System.out.println("waiter " + name + "分配制作任务"); for (int i=0;i<list.size();i++) { list.get(i).execute(); } } }
顾客类
package io.powerx.command; public class Customer { private String name; public Customer(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void orderBread(Waiter w,Cook c) { System.out.println(name+"order bread"); w.receive(new MakeBread(c)); } public void orderRoast(Waiter w,Cook c) { System.out.println(name+"order roast"); w.receive(new MakeRoast(c)); } public void orderChicken(Waiter w,Cook c) { System.out.println(name+"order chicken"); w.receive(new MakeChicken(c)); } }
执行测试代码,结果如下:
以上便是命令模式的标准实现,然而并没有达到顾客与厨师的解耦,我们的customer仍依赖于cook类,这tm就尴尬了,还不如直接在customer类中order*方法直接调cook的对应方法呢。只是学习设计模式切莫死搬硬套,我们只需要在waiter类中维护一个cook的list,由waiter决定哪个厨师来处理任务即可,这样我们的customer类就彻底与cook类解耦。
修改waiter类如下
package io.powerx.command; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Waiter2 { private String name; private int currentIndex ; private List<Cook> cooklist; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<MakeFood> getList() { return list; } public void setList(List<MakeFood> list) { this.list = list; } public Waiter2(String name, Cook ...cooks) { super(); this.name = name; this.cooklist = Arrays.asList(cooks); this.list = new ArrayList<>(); } private List<MakeFood> list; public void receive(MakeFood makeFood) { list.add(makeFood); } public void assign() { System.out.println("waiter " + name + "分配制作任务"); for (int i=0;i<list.size();i++) { list.get(i).execute(); } } public Cook chooseCook() { Cook cook = cooklist.get(currentIndex % cooklist.size()) ; currentIndex++; return cook; } public int getCurrentIndex() { return currentIndex; } public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; } public List<Cook> getCooklist() { return cooklist; } public void setCooklist(List<Cook> cooklist) { this.cooklist = cooklist; } }
同时顾客类的调用方式也发生了变化
package io.powerx.command; public class Customer2 { private String name; public Customer2(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void orderBread(Waiter2 w) { System.out.println(name+"order bread"); w.receive(new MakeBread(w.chooseCook())); } public void orderRoast(Waiter2 w) { System.out.println(name+"order roast"); w.receive(new MakeRoast(w.chooseCook())); } public void orderChicken(Waiter2 w) { System.out.println(name+"order chicken"); w.receive(new MakeChicken(w.chooseCook())); } }
执行测试代码结果如下:
改造后的类图如下:
最后讲一下命令模式的适用场景,如果有以下需求,可以考虑使用命令模式:
1、希望将行为请求者和行为实现者解耦,不直接打交道。
2、希望可以控制执行的命令列表,方便记录,撤销/重做以及事务等功能。
3、期待可以将请求排队,有序执行。
4、希望可以将请求组合使用。