设计模式介绍

就像我们以前用手柄按键玩游戏一样,这个游戏的控制方式——按键操作就像命令模式。命令模式虽然在编程开发中用的比较少,但是这种模式在日常生活种却经常用到,例如Ctrl+C和Ctrl+v。

命令模式是把逻辑实现与操作请求分离,降低耦合,方便扩展。命令模式是行为模式中的一种,以数据驱动的方式将命令对象用构造函数的方式传递给调用者。调用者再提供相应的实现,为命令执行提供操作方法。

在命令模式的实现过程中,重要的有以下几点:

  • 抽象命令类:声明执行命令的接口和方法;
  • 具体的命令实现类:接口类的具体 实现可以是一组相似的行为逻辑;
  • 实现者:给命令开发执行逻辑的具体实现类;
  • 调用者:处理命令、实现的具体操作者,负责对外提供命令服务。

命令模式简单使用

使用场景:餐厅点餐

命令模式的核心逻辑是调用方不需要关心具体的逻辑实现。在样例中,顾客只需要把点的菜交给服务员就可以,服务员再请初始烹饪。顾客不需要与厨师交流,只需要和服务员沟通就可以。

违背设计模式实现

在不使用设计模式的情况下,用一个类就可以实现。

如果不懂设计模式直接开发,虽然可以达到目的,但对于各项菜品的扩展、厨师实现及如何调用,会变得非常耦合且难以扩展。

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
package com.bestrookie.design;
import com.alibaba.fastjson.JSON;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @author bestrookie
* @version 1.0
* @date 2021/11/20 17:58
*/
public class XiaoEr {
private Map<Integer, String> cuisineMap = new ConcurrentHashMap<>();
public void order(int cuisine){
if (1 == cuisine){
cuisineMap.put(1,"广东菜");
}
if (2 == cuisine){
cuisineMap.put(2,"江苏菜");
}
if (3 == cuisine){
cuisineMap.put(3,"山东菜");
}
if (4 == cuisine){
cuisineMap.put(4,"川菜");
}
}
public void placeOrder(){
System.out.println("菜单: "+ JSON.toJSONString(cuisineMap));
}
}

可以看到有比较多的if语句判断类型,用于添加产品,维护这种代码需要付出大量的精力,而且实际业务的逻辑要比这复杂的多,如果都写在一个类里,会耦合的非常严重。

命令模式重构代码

命令模式可以将上述的模式拆解成三大块:命令、实现者和调用者。当有新的菜品或需要增加厨师时,就可以在指定的类架构下添加,外部的调用也会非常容易扩展。

image-20211120230104968

定义命定定义(菜品接口)
1
2
3
4
public interface ICuisine {
void cook();
}

具体命令实现(四种菜品)
1
2
3
4
5
6
7
8
9
10
public class GuangDongCuisine implements ICuisine {
private ICook cook;
public GuangDongCuisine(ICook cook){
this.cook = cook;
}
@Override
public void cook() {
cook.doCook();
}
}
1
2
3
4
5
6
7
8
9
10
public class JiangSuCuisine implements ICuisine {
private ICook cook;
public JiangSuCuisine(ICook cook){
this.cook = cook;
}
@Override
public void cook() {
cook.doCook();
}
}
1
2
3
4
5
6
7
8
9
10
public class ShanDongCuisine implements ICuisine {
private ICook cook;
public ShanDongCuisine(ICook cook){
this.cook = cook;
}
@Override
public void cook() {
cook.doCook();
}
}
1
2
3
4
5
6
7
8
9
10
public class SiChuangCuisine implements ICuisine {
private ICook cook;
public SiChuangCuisine(ICook cook){
this.cook = cook;
}
@Override
public void cook() {
cook.doCook();
}
}

以上是四种菜品的实现过程,在实现的类中都添加了一个厨师类(ICook),并使用这个类提供的方法操作命令(烹饪命令)cook.doCooking()。命令的实现过程可以按照逻辑添加补充,这里抽象得比较简单,只是模拟一个菜品烹饪的过程,相当于点菜的同时厨师开始烹饪。

定义实现者(厨师接口)
1
2
3
public interface ICook {
void doCook();
}
实现者具体实现(四种厨师)
1
2
3
4
5
6
public class GuangDongCook implements ICook {
@Override
public void doCook() {
System.out.println("广东菜");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bestrookie.design.cook.impl;

import com.bestrookie.design.cook.ICook;

/**
* @author bestrookie
* @version 1.0
* @date 2021/11/20 20:52
*/
public class JiangSuCook implements ICook {
@Override
public void doCook() {
System.out.println("江苏菜");
}
}
1
2
3
4
5
6
7
public class GuangDongCook implements ICook {
@Override
public void doCook() {
System.out.println("广东菜");
}
}

1
2
3
4
5
6
public class ShanDongCook implements ICook {
@Override
public void doCook() {
System.out.println("山东菜");
}
}
1
2
3
4
5
6
7
public class SiChuanCook implements ICook {
@Override
public void doCook() {
System.out.println("四川菜");
}
}

以上可以看到,当需要扩展厨师和菜品时,可以非常方便地添加,每一个类都具备了单一职责原则。

调用者
1
2
3
4
5
6
7
8
9
10
11
12
public class XiaoEr {
private List<ICuisine> cuisineList = new ArrayList<>();
public void order(ICuisine cuisine){
cuisineList.add(cuisine);
}
public synchronized void priceOrder(){
for (ICuisine iCuisine : cuisineList) {
iCuisine.cook();
}
cuisineList.clear();
}
}

在调用者的具体实现中,提供了菜品的添加和菜单执行烹饪任务。这个过程是命令模式的具体调用,通过外部接口调用,将菜品实现类和厨师实现类传递进来。

总结

命令模式分为命令、实现者和调用者。二这三块内容的拆分也是选择场景的因素,经过拆分,可以让逻辑具备单一职责的性质,便于扩展。与if语句相比,这种实现方式降低了耦合性,也方便其他命令和实现的扩展。但这种设计模式也带了一些问题,在各种命令与实现者的组合下,会扩展出很多的实现类,需要管理。

评论