2024050501-重学 Java 设计模式《实战命令模式》

news2024/12/26 20:47:55

重学 Java 设计模式:实战命令模式「模拟高档餐厅八大菜系,小二点单厨师烹饪场景」

一、前言

持之以恒的重要性

初学编程往往都很懵,几乎在学习的过程中会遇到各种各样的问题,哪怕别人那运行好好的代码,但你照着写完就报错。但好在你坚持住了,否则你可能看不到这篇文章。时间和成长就是相互关联着,你在哪条路上坚持走的久,就能看见那条的终点有多美,但如果你浪费了一次又一次努力的机会,那么你也会同样错过很多机遇,因为你的路换了。坚持学习、努力成长,持以恒的付出一定会有所收获。

学习方法的重要性

不会学习往往会耽误很多时间,又没有可观的收成。但不会学习有时候是因为造成的,尤其是学习视频、书籍资料、技术文档等,如果只是看了却不是实际操作验证,那么真的很难把别人的知识让自己吸收,即使是当时感觉会了也很快就会忘记。时而也经常会有人找到你说;“这个我不知道,你先告诉我,过后我就学。”但过后你学了吗?

你愿意为一个知识盲区付出多长时间

你心里时而会蹦出这样的词吗;太难了我不会找个人帮一下吧放弃了放弃了,其实谁都可能遇到很不好解决的问题,也是可以去问去咨询的。但,如果在这之前你没有在自己的大脑中反复的寻找答案,那么你的大脑中就不会形成一个凸点的知识树,缺少了这个学习过程也就缺少了查阅各种资料给自己大脑填充知识的机会,哪怕是问到了答案最终也会因时间流逝而忘记。

二、开发环境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三个,可以通过关注公众号bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
工程描述
itstack-demo-design-14-01使用一坨代码实现业务需求
itstack-demo-design-14-02通过设计模式优化代码结构,增加扩展性和维护性

三、命令模式介绍

命令模式,图片来自 refactoringguru.cn

  • 图片来自:https://refactoringguru.cn/design-patterns/command

命令模式在我们通常的互联网开发中相对来说用的比较少,但这样的模式在我们的日常中却经常使用到,那就是Ctrl+CCtrl+V。当然如果你开发过一些桌面应用,也会感受到这样设计模式的应用场景。从这样的模式感受上,可以想到这是把逻辑实现与操作请求进行分离,降低耦合方便扩展。

命令模式是行为模式中的一种,以数据驱动的方式将命令对象,可以使用构造函数的方式传递给调用者。调用者再提供相应的实现为命令执行提供操作方法。可能会感觉这部分有一些饶,可以通过对代码的实现进行理解,在通过实操来熟练。

在这个设计模式的实现过程中有如下几个比较重要的点;

  1. 抽象命令类;声明执行命令的接口和方法
  2. 具体的命令实现类;接口类的具体实现,可以是一组相似的行为逻辑
  3. 实现者;也就是为命令做实现的具体实现类
  4. 调用者;处理命令、实现的具体操作者,负责对外提供命令服务

四、案例场景模拟

场景模拟;大餐厅点餐场景

在这个案例中我们模拟在餐厅中点餐交给厨师👨‍🍳烹饪的场景

命令场景的核心的逻辑是调用方与不需要去关心具体的逻辑实现,在这个场景中也就是点餐人员只需要把需要点的各种菜系交个小二就可以,小二再把各项菜品交给各个厨师进行烹饪。也就是点餐人员不需要跟各个厨师交流,只需要在统一的环境里下达命令就可以。

在这个场景中可以看到有不同的菜品;山东(鲁菜)、四川(川菜)、江苏(苏菜)、广东(粤菜)、福建(闽菜)、浙江(浙菜)、湖南(湘菜),每种菜品都会有不同的厨师👩‍🍳进行烹饪。而客户并不会去关心具体是谁烹饪,厨师也不会去关心谁点的餐。客户只关心早点上菜,厨师只关心还有多少个菜要做。而这中间的衔接的过程,由小二完成。

那么在这样的一个模拟场景下,可以先思考🤔哪部分是命令模式的拆解,哪部分是命令的调用者以及命令的实现逻辑。

五、用一坨坨代码实现

不考虑设计模式的情况下,在做这样一个点单系统,有一个类就够了

像是这样一个复杂的场景,如果不知道设计模式直接开发,也是可以达到目的的。但对于后续的各项的菜品扩展、厨师实现以及如何调用上会变得非常耦合难以扩展。

1. 工程结构

itstack-demo-design-14-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── XiaoEr.java
  • 这里只有一个饭店小二的类,通过这样的一个类实现整个不同菜品的点单逻辑。

2. 代码实现

public class XiaoEr {

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

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

    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() {
        logger.info("菜单:{}", JSON.toJSONString(cuisineMap));
    }

}
  • 在这个类的实现中提供了两个方法,一个方法用于点单添加菜品order(),另外一个方法展示菜品的信息placeOrder()
  • 从上面可以看到有比较多的if语句判断类型进行添加菜品,那么对于这样的代码后续就需要大量的经历进行维护,同时可能实际的逻辑要比这复杂的多。都写在这样一个类里会变得耦合的非常严重。

六、命令模式重构代码

接下来使用命令模式来进行代码优化,也算是一次很小的重构。

命令模式可以将上述的模式拆解三层大块,命令、命令实现者、命令的调用者,当有新的菜品或者厨师扩充时候就可以在指定的类结构下进行实现添加即可,外部的调用也会非常的容易扩展。

1. 工程结构

itstack-demo-design-14-02
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo.design
    │           ├── cook
    │           │	├── impl
    │           │	│   ├── GuangDongCook.java
    │           │	│   ├── JiangSuCook.java
    │           │	│   ├── ShanDongCook.java
    │           │	│   └── SiChuanCook.java
    │           │	└── ICook.java
    │           ├── cuisine
    │           │	├── impl
    │           │	│   ├── GuangDoneCuisine.java
    │           │	│   ├── JiangSuCuisine.java
    │           │	│   ├── ShanDongCuisine.java
    │           │	│   └── SiChuanCuisine.java
    │           │	└── ICuisine.java
    │           └── XiaoEr.java
    └── test
        └── java
            └── org.itstack.demo.test
                └── ApiTest.java

命令模式模型结构

在这里插入图片描述

  • 从上图可以看到整体分为三大块;命令实现(菜品)、逻辑实现(厨师)、调用者(小二),以上这三面的实现就是命令模式的核心内容。
  • 经过这样的拆解就可以非常方面的扩展菜品、厨师,对于调用者来说这部分都是松耦合的,在整体的框架下可以非常容易加入实现逻辑。

2. 代码实现

2.1 抽象命令定义(菜品接口)
/**
 * 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
 * 公众号:bugstack虫洞栈
 * Create by 小傅哥(fustack) @2020
 *
 * 菜系
 * 01、山东(鲁菜)——宫廷最大菜系,以孔府风味为龙头。
 * 02、四川(川菜)——中国最有特色的菜系,也是民间最大菜系。
 * 03、江苏(苏菜)——宫廷第二大菜系,古今国宴上最受人欢迎的菜系。
 * 04、广东(粤菜)——国内民间第二大菜系,国外最有影响力的中国菜系,可以代表中国。
 * 05、福建(闽菜)——客家菜的代表菜系。
 * 06、浙江(浙菜)——中国最古老的菜系之一,宫廷第三大菜系。
 * 07、湖南(湘菜)——民间第三大菜系。
 * 08、安徽(徽菜)——徽州文化的典型代表。
 */
public interface ICuisine {

    void cook(); // 烹调、制作

}
  • 这是命令接口类的定义,并提供了一个烹饪方法。后面会选四种菜品进行实现。
2.2 具体命令实现(四种菜品)

广东(粤菜)

public class GuangDoneCuisine implements ICuisine {

    private ICook cook;

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

    public void cook() {
        cook.doCooking();
    }

}

江苏(苏菜)

public class JiangSuCuisine implements ICuisine {

    private ICook cook;

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

    public void cook() {
        cook.doCooking();
    }

}

山东(鲁菜)

public class ShanDongCuisine implements ICuisine {

    private ICook cook;

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

    public void cook() {
        cook.doCooking();
    }

}

四川(川菜)

public class SiChuanCuisine implements ICuisine {

    private ICook cook;

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

    public void cook() {
        cook.doCooking();
    }

}
  • 以上是四种菜品的实现,在实现的类中都有添加了一个厨师类(ICook),并通过这个类提供的方法进行操作命令(烹饪菜品)cook.doCooking()
  • 命令的实现过程可以是按照逻辑进行添加补充,目前这里抽象的比较简单,只是模拟一个烹饪的过程,相当于同时厨师进行菜品烹饪。
2.3 抽象实现者定义(厨师接口)
public interface ICook {

    void doCooking();

}
  • 这里定义的是具体的为命令的实现者,这里也就是菜品对应的厨师烹饪的指令实现。
2.4 实现者具体实现(四类厨师)

粤菜,厨师

public class GuangDongCook implements ICook {

    private Logger logger = LoggerFactory.getLogger(ICook.class);

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

}

苏菜,厨师

public class JiangSuCook implements ICook {

    private Logger logger = LoggerFactory.getLogger(ICook.class);

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

}

鲁菜,厨师

public class ShanDongCook implements ICook {

    private Logger logger = LoggerFactory.getLogger(ICook.class);

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

}

苏菜,厨师

public class SiChuanCook implements ICook {

    private Logger logger = LoggerFactory.getLogger(ICook.class);

    public void doCooking() {
        logger.info("四川厨师,烹饪川菜,中国最有特色的菜系,也是民间最大菜系。");
    }

}
  • 这里是四类不同菜品的厨师👩‍🍳,在这个实现的过程是模拟打了日志,相当于通知了厨房里具体的厨师进行菜品烹饪。
  • 从以上可以看到,当我们需要进行扩从的时候是可以非常方便的进行添加的,每一个类都具备了单一职责原则。
2.5 调用者(小二)
public class XiaoEr {

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

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

    public void order(ICuisine cuisine) {
        cuisineList.add(cuisine);
    }

    public synchronized void placeOrder() {
        for (ICuisine cuisine : cuisineList) {
            cuisine.cook();
        }
        cuisineList.clear();
    }

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

3. 测试验证

3.1 编写测试类
@Test
public void test(){

    // 菜系 + 厨师;广东(粤菜)、江苏(苏菜)、山东(鲁菜)、四川(川菜)
    ICuisine guangDoneCuisine = new GuangDoneCuisine(new GuangDongCook());
    JiangSuCuisine jiangSuCuisine = new JiangSuCuisine(new JiangSuCook());
    ShanDongCuisine shanDongCuisine = new ShanDongCuisine(new ShanDongCook());
    SiChuanCuisine siChuanCuisine = new SiChuanCuisine(new SiChuanCook());

    // 点单
    XiaoEr xiaoEr = new XiaoEr();
    xiaoEr.order(guangDoneCuisine);
    xiaoEr.order(jiangSuCuisine);
    xiaoEr.order(shanDongCuisine);
    xiaoEr.order(siChuanCuisine);

    // 下单
    xiaoEr.placeOrder();
}
  • 这里可以主要观察菜品厨师的组合;new GuangDoneCuisine(new GuangDongCook());,每一个具体的命令都拥有一个对应的实现类,可以进行组合。
  • 当菜品和具体的实现定义完成后,由小二进行操作点单,xiaoEr.order(guangDoneCuisine);,这里分别添加了四种菜品,给小二。
  • 最后是下单,这个是具体命令实现的操作,相当于把小二手里的菜单传递给厨师。当然这里也可以提供删除和撤销,也就是客户取消了自己的某个菜品。
3.2 测试结果
22:12:13.056 [main] INFO  org.itstack.demo.design.cook.ICook - 广东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头
22:12:13.059 [main] INFO  org.itstack.demo.design.cook.ICook - 江苏厨师,烹饪苏菜,宫廷第二大菜系,古今国宴上最受人欢迎的菜系。
22:12:13.059 [main] INFO  org.itstack.demo.design.cook.ICook - 山东厨师,烹饪鲁菜,宫廷最大菜系,以孔府风味为龙头
22:12:13.059 [main] INFO  org.itstack.demo.design.cook.ICook - 四川厨师,烹饪川菜,中国最有特色的菜系,也是民间最大菜系。

Process finished with exit code 0
  • 从上面的测试结果可以看到,我们已经交给调用者(小二)的点单,由不同的厨师具体实现(烹饪)。
  • 此外当我们需要不同的菜品时候或者修改时候都可以非常方便的添加和修改,在具备单一职责的类下,都可以非常方便的扩展。

七、总结

  • 从以上的内容和例子可以感受到,命令模式的使用场景需要分为三个比较大的块;命令实现调用者,而这三块内容的拆分也是选择适合场景的关键因素,经过这样的拆分可以让逻辑具备单一职责的性质,便于扩展。
  • 通过这样的实现方式与if语句相比,降低了耦合性也方便其他的命令和实现的扩展。但同时这样的设计模式也带来了一点问题,就是在各种命令与实现的组合下,会扩展出很多的实现类,需要进行管理。
  • 设计模式的学习一定要勤加练习,哪怕最开始是模仿实现也是可以的,多次的练习后再去找到一些可以优化的场景,并逐步运用到自己的开发中。提升自己对代码的设计感觉,让代码结构更加清晰易扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1794895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络基础知识】

首先举一个生活化的例子&#xff0c;当你和朋友打电话时&#xff0c;你可能会使用三次握手和四次挥手的过程进行类比&#xff1a; 三次握手&#xff08;Three-Way Handshake&#xff09;&#xff1a; 你打电话给朋友&#xff1a;你首先拨打你朋友的电话号码并等待他接听。这就…

LeetCode刷题之HOT100之组合总和

2024/6/3 周一&#xff0c;工作日的第一天。昨晚梦到被导师说去实验室不积极哈哈哈&#xff0c;风扇开到二级&#xff0c;早上被吹醒。买的书马上快要到了。上午刚来准备刷题&#xff0c;结果去搞了一下数据库sql&#xff0c;做的差不多了&#xff0c;还差点格式转换就差不多出…

【WP】猿人学_13_入门级cookie

https://match.yuanrenxue.cn/match/13 抓包分析 抓包分析发现加密参数是cookie中有一个yuanrenxue_cookie 当cookie过期的时候&#xff0c;就会重新给match/13发包&#xff0c;这个包返回一段js代码&#xff0c;应该是生成cookie的 <script>document.cookie(y)(u)(a…

【Text2SQL】评估 LLM 的 Text2SQL 能力

论文&#xff1a;Evaluating the Text-to-SQL Capabilities of Large Language Models ⭐⭐⭐⭐ arXiv:2204.00498 一、论文速读 本论文尝试了多种 prompt 结构&#xff0c;并且评估了他们在 Codex 和 GPT-3 上的表现。下面介绍这些 prompt 结构&#xff1a; 二、不同的 prom…

VsQt单元测试目录的管理方式

正常项目的文件管理方式 正常项目的目录&#xff0c;是由文件系统中实际的文件夹进行分类管理的。 但是如果单元测试用实际文件夹管理的话&#xff0c;会出现问题&#xff0c;就是被测类太多了&#xff0c;用文件系统管理的话&#xff0c;不太方面查看&#xff0c;如下图所示。…

AI数字人本地算力直播怎么做?青否数字人教您快速搭建直播间!

一、青否数字人直播分为两种&#xff0c;一种是云端渲染另外一种就是本地渲染 使用本地算力来进行直播间互动&#xff0c;本地算力能够使商家拥有更低成本运营 在独立部署数字人SaaS系统后&#xff0c;其他的数字人厂商是【云服务器】【算力服务器】相结合的方式&#xff0c;以…

31|HTTP3:甩掉TCP、TLS 的包袱,构建高效网络

前面两篇文章我们分析了HTTP/1和HTTP/2&#xff0c;在HTTP/2出现之前&#xff0c;开发者需要采取很多变通的方式来解决HTTP/1所存在的问题&#xff0c;不过HTTP/2在2018年就开始得到了大规模的应用&#xff0c;HTTP/1中存在的一大堆缺陷都得到了解决。 HTTP/2的一个核心特性是…

App Inventor 2 复制屏幕功能,界面设计更便捷,避免误删组件

“复制屏幕”功能全新上线&#xff0c;中文网独有&#xff08;MIT没有此功能&#xff09;&#xff0c;可以复制屏幕中的普通组件、图片、附件、拓展、代码块。更多升级详情可查看发布日志。 下面演示一下屏幕的复制效果&#xff1a; 1、Screen1屏幕上有若干组件、及一个SQLit…

支付宝小众玩法 爱溜达的人不容错过

创建一个简单的程序来帮助用户管理他们的图片&#xff0c;例如筛选分辨率合适、尺寸适中的图片来准备上传&#xff0c;这是一个技术上合理且有益的方向。例如&#xff0c;一个Python脚本使用Pillow库来检查文件夹中图片的尺寸&#xff1a; from PIL import Image import os# 根…

如何永久擦除Android手机中的所有个人数据?

在这个数字化的时代&#xff0c;确保您的个人数据的安全和隐私至关重要。如果您计划出售或回收您的Android手机&#xff0c;了解如何正确擦除Android手机是至关重要的。本综合指南将引导您通过安全擦除Android手机的分步过程&#xff0c;以保护您的敏感信息。 手机是极其敏感的…

冲刺面试加油

1、HTML语义化&#xff1f; 对于开发者而言&#xff0c;语义化标签有着更好的页面结构&#xff0c;有利于代码的开发编写和后期的维护。 对于用户而言&#xff0c;当网络卡顿时有良好的页面结构&#xff0c;有利于增加用户的体验。 对于爬虫来说&#xff0c;有利于搜索引擎的…

vue中使用svg图像

一 、svg图像是什么 SVG&#xff08;可缩放矢量图形&#xff09;是一种图像格式&#xff0c;它以XML文档的形式存在&#xff0c;用以描述图像中的形状、线条、文本和颜色等元素。由于其基于矢量的特性&#xff0c;SVG图像在放大或改变尺寸时能够保持图形质量不受影响。这种格式…

【Altium】AD-原理图中使用多通道问题

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决在原理图主图中&#xff0c;对子图使用多通道命令后&#xff0c;子图未形成多个通道的问题。 2、 问题场景 客户手中的工程文件&#xff0c;主图中对子图使用了多通道命令&#xff0c;子图图纸并未形成多通道…

反转链表的三种方法--面试必考(图例超详细解析,小白一看就会!!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 头插法 --- 创建新的链表 ⭐ 迭代法 --- 三指针 ⭐ 递归法 四、总结与提炼 五、共勉 一、前言 反转链表这道题&#xff0c;可以说是--链表专题--&#xff0c;最经典的一道题&#xff0c;也是在面试中频率最高的一道题目&…

上位机图像处理和嵌入式模块部署(f407 mcu vs f103)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于一部分嵌入式场景来说&#xff0c;f103其实已经足够了&#xff0c;特别是要求不高的低速场合。如果开发的代码比较多&#xff0c;还可以选用更…

MySQL排序操作

025排序操作 select .. from .. order by 字段 asc/descselect empno, ename, sal from emp order by sal asc;asc 不写的话&#xff0c;默认升序 多个字段排序 查询员工的编号、姓名、薪资&#xff0c;按照薪资升序排列&#xff0c;如果薪资相同的&#xff0c;再按照姓名升…

【养老政策问答】银发经济你怎么看?

概述 养老政策问答是一个专门为老年人设计的一站式问答服务平台。该应用旨在为用户提供关于养老政策的全面信息和专业解答&#xff0c;帮助老年人理解和获取相关政策的详细内容&#xff0c;享受无忧的老年生活。用户可以通过此应用获取关于基本养老保险、养老保险的缴费与领取…

HiveQL性能调优-概览

一、铺垫 1、HiveQL 在执行时会转化为各种计算引擎的能够运行的算子&#xff0c;这里以mr引擎为切入点&#xff0c;要想让HiveQL 的效率更高&#xff0c;就要理解HiveQL 是如何转化为MapReduce任务的 2、hive是基于hadoop的&#xff0c;分布式引擎采用mr、spark、tze&#x…

升级 WordPress 时“另一更新正在进行”的 3 种解决办法

你是否尝试过升级 WordPress 版本时收到“另一更新正在进行”错误&#xff1f;本文将介绍 3 中方法&#xff0c;迅速解除错误&#xff0c;以便再次升级 WordPress 版本。 升级至关重要&#xff0c;但并不总是按预期进行&#xff0c;因此应尽快完成对 WordPress 更新的故障排除。…

Spring Boot 集成 zxing 生成条形码与二维码

前面我们知道了怎么通过 使用 zxing 生成二维码以及条形码&#xff0c; 由于我们现在都是 web 端的项目了&#xff0c;那么我们看下怎么使用 Spring Boot 集成然后返回给前端展示&#xff1a; 工程源码 对应的工程源码我放到了这里&#xff1a;github源码路径&#xff0c;点击…