命令模式(Command)

将命令和实际执行者分离,对外提供统一表现

命令模式

概述

将命令封装成对象中,具有以下作用:

  • 使用命令来参数化其它对象
  • 将命令放入队列中进行排队
  • 将命令的操作记录到日志中
  • 支持可撤销的操作

类图

  • Command:命令
  • Receiver:命令接收者,也就是命令真正的执行者
  • Invoker:通过它来调用命令
  • Client:可以设置命令与命令的接收者

场景模拟:

顾客点菜,小二记下菜,点完之后小二通知各种菜系的厨师做菜

这里的小二就是 Invoker,顾客就是 Client,菜系就是 Command,厨师就是 Receiver

具体实现

不使用命令模式实现

后续如果添加菜品,用 if 不好维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class XiaoEr {

private final Logger logger = LoggerFactory.getLogger(XiaoEr.class);

private final Map<Integer, String> cuisineMap = new ConcurrentHashMap<>();

public void order(int cuisine) {
// 广东(粤菜)
if (1 == cuisine) {
cuisineMap.put(1, "广东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头");
}

// 江苏(苏菜)
if (2 == cuisine) {
cuisineMap.put(2, "江苏厨师,烹饪苏菜,宫廷第二大菜系,古今国宴上最受人欢迎的菜系。");
}
}

public void placeOrder() {
Gson gson = new Gson();
logger.info("菜单:{}", gson.toJson(cuisineMap));
}

public static void main(String[] args) {
XiaoEr xiaoEr = new XiaoEr();
xiaoEr.order(1);
xiaoEr.order(2);

xiaoEr.placeOrder();
}
}

使用命令模式

维护一个菜系( ICuisine )接口,还有一个厨师( ICook )接口,菜系中调用厨师做饭;

ICuisine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author :hc
* @date :Created in 2022/3/13 21:35
* @modified
* 菜系
* 1.广东(粤菜)——国内民间第二大菜系,国外最有影响力的中国菜系,可以代表中国。
* 2.江苏(苏菜)——宫廷第二大菜系,古今国宴上最受人欢迎的菜系。
*/
public interface ICuisine {

/**
* 烹调,具体的做饭逻辑(其实就是找具体的厨师做菜)
*/
void cook();
}

GuangDoneCuisine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author :hc
* @date :Created in 2022/3/13 21:37
* @modified
* 广东菜
*/
public class GuangDoneCuisine implements ICuisine {

private final ICook cook;

public GuangDoneCuisine(ICook cook) {
this.cook = cook;
}

@Override
public void cook() {
this.cook.doCooking();
}
}

JiangSuCuisine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author :hc
* @date :Created in 2022/3/13 21:39
* @modified
* 江苏菜
*/
public class JiangSuCuisine implements ICuisine {

private final ICook cook;

public JiangSuCuisine(ICook cook) {
this.cook = cook;
}

@Override
public void cook() {
this.cook.doCooking();
}
}

厨师

1
2
3
4
5
6
7
public interface ICook {

/**
* 厨师做饭
*/
void doCooking();
}

广东厨师

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author :hc
* @date :Created in 2022/3/13 21:40
* @modified
* 广东厨师
*/
public class GuangDongCook implements ICook {

private final Logger logger = LoggerFactory.getLogger(GuangDongCook.class);

@Override
public void doCooking() {
logger.info("广东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头");
}
}

江苏厨师

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author :hc
* @date :Created in 2022/3/13 21:42
* @modified
* 江苏厨师
*/
public class JiangSuCook implements ICook {

private final Logger logger = LoggerFactory.getLogger(JiangSuCook.class);

@Override
public void doCooking() {
logger.info("江苏厨师,烹饪苏菜,宫廷第二大菜系,古今国宴上最受人欢迎的菜系。");
}
}

小二,向厨师下达命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* @author :hc
* @date :Created in 2022/3/13 21:44
* @modified
* 小二,向厨师下达命令的
*/
public class XiaoEr {

private final Logger logger = LoggerFactory.getLogger(XiaoEr.class);

private final List<ICuisine> cuisineList = new ArrayList<>();

/**
* 客户点菜,小二加到做菜列表里
* @param cuisine 菜系
*/
public void order(ICuisine cuisine) {
cuisineList.add(cuisine);
}

/**
* 客户点完菜了,小二告诉厨师可以做了
*/
public synchronized void placeOrder() {
for (ICuisine cuisine : cuisineList) {
cuisine.cook();
}
cuisineList.clear();
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainTest {

public static void main(String[] args) {
// 菜系 + 厨师
GuangDoneCuisine guangDoneCuisine = new GuangDoneCuisine(new GuangDongCook());
JiangSuCuisine jiangSuCuisine = new JiangSuCuisine(new JiangSuCook());

// 点菜
XiaoEr xiaoEr = new XiaoEr();
xiaoEr.order(guangDoneCuisine);
xiaoEr.order(jiangSuCuisine);

// 下单 厨师开始做菜
xiaoEr.placeOrder();
}
}

输出:

1
2
22:23:04.644 [main] INFO  icu.sunnyc.cook.impl.GuangDongCook - 广东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头
22:23:04.650 [main] INFO icu.sunnyc.cook.impl.JiangSuCook - 江苏厨师,烹饪苏菜,宫廷第二大菜系,古今国宴上最受人欢迎的菜系。

调用时序图

可以看到,顾客 new 了俩菜,也就是 new 了俩命令对象(ConcreteCommand),将对象以参数形式传入小二,小二(Invoker)接收到,调用 Command,然后实际的厨师(receiver)开始做菜了。