1.引子
晚上下班都有吃宵夜的习惯,路过烧烤摊不禁想撸一串。这里用代码表示就是:
烧烤摊摊主:
public class Barbecuer {
public void roastChickenWings(int num) {
System.out.print( num + "串烤鸡翅...");
}
public void roastChickenLeg(int num) {
System.out.print( num + "只烤鸡腿...");
}
public void roastHotDog(int num) {
System.out.print( num + "根热狗...");
}
}
这里分别用三个方法表示烤三种不同的食物,方法参数是数量。
我来到烧烤摊:
public class Client {
public static void main(String[] args) {
Barbecuer barbecuer = new Barbecuer();
System.out.print("老板,我要");
barbecuer.roastChickenWings(2);
barbecuer.roastChickenLeg(1);
barbecuer.roastHotDog(5);
}
}
我要了2只烤翅,1只鸡腿和5根热狗。
貌似挺容易的。可是一到周末的晚上,撸串的人特别多,老板一个人忙不过来,不是数量错了就是没怎么熟,导致印象极差。究其原因就是,摊主充当着多种角色,他即要算账收钱,又要自己烤,导致自己忙不过来。我们在代码里虽然省略了算账了过程,但是这也说明摊主类极不合理,违背了“单一职责”原则。因此需要拆分。
假设摊主雇了一个服务员,专门来收集订单和算账,摊主自己安心烧烤就行。这里服务员是连接顾客和摊主的桥梁,起到传递消息的作用。那么这个消息其实就是顾客的订单,就是上面的“2只烤翅”、“1只鸡腿”、“5根热狗”;顾客和服务员关心的只是烤的东西,而用什么材料、怎么烤才好吃那是摊主自己的事儿,所以对于订单信息可以用类来表示,方便顾客和服务员请求,而具体的实现则在摊主类里面。用代码来表示就是:
烧烤摊摊主类不变:
public class Barbecuer {
public void roastChickenWings(int num) {
System.out.print( num + "串烤鸡翅...");
}
public void roastChickenLeg(int num) {
System.out.print( num + "只烤鸡腿...");
}
public void roastHotDog(int num) {
System.out.print( num + "根热狗...");
}
}
订单接口:
public abstract class Command {
public abstract void executCommand();
}
定义为接口,是对服务员隐藏细节。表示是烤鸡翅,烤鸡腿还是烤火腿肠等;
烤鸡腿类:
import com.design.command1.Barbecuer;
public class RoastChickenLeg extends Command {
private Barbecuer barbecuer;
private int num; // 订单数量
public RoastChickenLeg(Barbecuer barbecuer, int num) {
this.barbecuer = barbecuer;
this.num = num;
}
@Override
public void executCommand() {
barbecuer.roastChickenLeg(num); // 具体由摊主自己来烤
}
@Override
public String toString() {
return num + "只烤鸡腿"; // 方便打日志
}
}
烤鸡翅类:
import com.design.command1.Barbecuer;
public class RoastChickenWing extends Command {
private Barbecuer barbecuer;
private int num;
public RoastChickenWing(Barbecuer barbecuer, int num) {
this.barbecuer = barbecuer;
this.num = num;
}
@Override
public void executCommand() {
barbecuer.roastChickenWings(num);
}
@Override
public String toString() {
return num + "只烤鸡翅";
}
}
烤火腿肠类:
import com.design.command1.Barbecuer;
public class RoastHotDog extends Command {
private Barbecuer barbecuer;
private int num;
public RoastHotDog(Barbecuer barbecuer, int num) {
this.barbecuer = barbecuer;
this.num = num;
}
@Override
public void executCommand() {
barbecuer.roastHotDog(num);
}
@Override
public String toString() {
return num + "只烤热狗";
}
}
服务员类:
public class Waiter {
List<Command> commandList = new ArrayList<Command>();
// 增加订单
public void addOrder(Command command) {
System.out.println("新增订单:" + command + ", 订单时间:" + new Date());
commandList.add(command);
}
// 取消订单
public void disOrder(Command command) {
System.out.println("取消订单:" + command + ", 订单时间:" + new Date());
commandList.remove(command);
}
// 通知后厨
public void notifyChief(){
for (Command command : commandList) {
command.executCommand();
}
}
}
服务员类里面用一个list来放置所有的订单信息,而后通过notifyChief()方法通过订单的方式给摊主。这里,我们通过Command接口对摊主类进行解耦,屏蔽了实现的细节,而后只需要扩展Command接口即可。另外通过addOrder()和disOrder()方法来增加和取消订单。
客人类:
import com.design.command1.Barbecuer;
public class Client {
public static void main(String[] args) {
Barbecuer barbecuer = new Barbecuer(); // 摊主对象
RoastChickenLeg roastChickenLeg = new RoastChickenLeg(barbecuer, 10); // 10只鸡腿
RoastChickenWing roastChickenWing = new RoastChickenWing(barbecuer, 5); // 5只鸡翅
RoastHotDog roastHotDog = new RoastHotDog(barbecuer, 2); // 2根热狗
Waiter xiaoMing = new Waiter();
xiaoMing.addOrder(roastChickenLeg);
xiaoMing.addOrder(roastChickenWing);
xiaoMing.addOrder(roastHotDog);
xiaoMing.notifyChief(); // 服务员xiaoMing通知摊主
System.out.println();
System.out.println("=============================");
xiaoMing.disOrder(roastChickenLeg); // 取消鸡腿
}
}
运行结果:
新增订单:10只烤鸡腿, 订单时间:Tue Dec 04 23:13:05 CST 2018
新增订单:5只烤鸡翅, 订单时间:Tue Dec 04 23:13:05 CST 2018
新增订单:2只烤热狗, 订单时间:Tue Dec 04 23:13:05 CST 2018
10只烤鸡腿...5串烤鸡翅...2根热狗...
=============================
取消订单:10只烤鸡腿, 订单时间:Tue Dec 04 23:13:05 CST 2018
这种模式就是命令模式。
2. 命令模式的原理
顾客通过具体命令的形式把请求提交到服务员的订单列表里,而服务员则通过接口隐藏命令的细节,通过各命令具体实现类传递给烧烤摊摊主,让他来实现烧烤的过程;这种将一个请求封装为一个对象,从而使得可把请求对象作为参数传递的情况就是命令模式。
3.命令模式的适用场景
实际中很少用到。如果需要,可以互相讨论一下诶。
4.命令模式的特点
最大的特点在于,把请求的动作作为一个对象来传递,而这个对象经过服务员通过接口给屏蔽了细节,最后由请求的这个对象传递给烧烤摊摊主。命令模式可以把服务员和烧烤摊摊主解耦。
5.参考资料
《大话设计模式》