命令模式(Command)

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

命令模式

概述

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

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

类图

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

场景模拟:

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

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

具体实现

不使用命令模式实现

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

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

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

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

GuangDoneCuisine

/**
 * @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

/**
 * @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();
    }
}

厨师

public interface ICook {

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

广东厨师

/**
 * @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("广东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头");
    }
}

江苏厨师

/**
 * @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("江苏厨师,烹饪苏菜,宫廷第二大菜系,古今国宴上最受人欢迎的菜系。");
    }
}

小二,向厨师下达命令

/**
 * @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();
    }
}

测试

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();
    }
}

输出:

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)开始做菜了。


命令模式(Command)
https://www.powercheng.fun/articles/346cfbe2/
作者
powercheng
发布于
2022年3月13日
许可协议