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

重学 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.mzph.cn/bicheng/25801.shtml

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

相关文章

Python数据分析与机器学习在电子商务推荐系统中的应用

文章目录 &#x1f4d1;引言一、推荐系统的类型二、数据收集与预处理2.1 数据收集2.2 数据预处理 三、基于内容的推荐3.1 特征提取3.2 计算相似度3.3 推荐物品 四、协同过滤推荐4.1 基于用户的协同过滤4.2 基于物品的协同过滤 五、混合推荐与评估推荐系统5.1 结合推荐结果5.2 评…

Qwen2本地部署的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

网络安全技术实验一 信息收集和漏洞扫描

一、实验目的和要求 了解信息搜集和漏洞扫描的一般步骤&#xff0c;利用Nmap等工具进行信息搜集并进行综合分析&#xff1b;掌握TCP全连接扫描、TCP SYN扫描的原理,利用Scapy编写网络应用程序&#xff0c;开发端口扫描功能模块&#xff1b;使用漏洞扫描工具发现漏洞并进行渗透测…

8款高效电脑维护与多媒体工具合集!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://h5.cxyhub.com/?invitationhmeEo7 1. 系统安装利器——WinNTSetup 系统安装利器&#xff0c;目前最好用的系统安装器&#xff0c;Windows系统安装部署工具。支持所…

跟我学,数据结构和组原真不难

我个人认为408中计算机组成原理和数据结构最难 难度排行是计算机组成原理>数据结构>操作系统>计算机网络。 计算机组成原理比较难的原因是&#xff0c;他涉及的硬件的知识比较多&#xff0c;这对于大家来说难度就很高了&#xff0c;特别是对于跨考的同学来说&#x…

ABB机械人模型下载

可以下载不同格式的 https://new.abb.com/products/robotics/zh/robots/articulated-robots/irb-6700 step的打开各部件是分开的&#xff0c;没有装配在一起&#xff0c;打开看单个零件时&#xff0c;我们会发现其各零件是有装配的定位关系的。 新建一个装配环境&#xff0c;点…

【qt】MDI多文档界面开发

MDI多文档界面开发 一.应用场景二.界面设计三.界面类设计四.实现功能1.新建文档2.打开文件3.关闭所有4.编辑功能5.MDI页模式6.瀑布展开模式7.平铺模式 五.总结 一.应用场景 类似于vs的界面功能,工具栏的功能可以对每个文档使用! 二.界面设计 老规矩,边做项目边学! 目标图: 需…

【JMeter接口测试工具】第二节.JMeter项目实战(上)【实战篇】

文章目录 前言项目实战零、接口测试流程一、测试数据准备二、接口功能测试三、掌握测试用例编写四、自动化脚本架构搭建总结 前言 零、接口测试流程 1、制定测试计划,分配任务 2、从 API 文档中提取接口清单&#xff1a;对 API 文档简化,提高测试效率,接口清单就是对 API 文档…

【研发日记】Matlab/Simulink软件优化(三)——利用NaNFlag为数据处理算法降阶

文章目录 前言 背景介绍 初始算法 优化算法 分析和应用 总结 前言 见《【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩》 见《【研发日记】Matlab/Simulink软件优化(二)——通信负载柔性均衡算法》 背景介绍 在一个嵌入式软件开发项目中&#xff0c;需要开…

OpenAI与核聚变公司寻求合作,白宫拨款1.8亿美元用于核聚变商业化研究

在当下&#xff0c;由 AI 引发的新一轮能源危机已经不再是一个小概率的「黑天鹅」事件&#xff0c;而是一头正在向我们猛冲而来的「灰犀牛」。 Helion Energy&#xff0c;是一家总部位于美国华盛顿州埃弗雷特的能源创业公司。 3.5研究测试&#xff1a;hujiaoai.cn 4研究测试&am…

Mysql的底层实现逻辑

Mysql5.x和Mysql8性能的差异 整体性能有所提高&#xff0c; 在非高并发场景下&#xff0c;他们2这使用区别不大&#xff0c;性能没有明显的区别。 只有高并发时&#xff0c;mysql8才体现他的优势。 2. Mysql数据存储结构Innodb逻辑结构 数据选用B树结构存储数据&#xff0…

如何画系统架构图学习

原文链接:https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/%E4%BB%8E%200%20%E5%BC%80%E5%A7%8B%E5%AD%A6%E6%9E%B6%E6%9E%84/51%20%E5%A6%82%E4%BD%95%E7%94%BB%E5%87%BA%E4%BC%98%E7%A7%80%E7%9A%84%E8%BD%AF%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E5%9B%BE%EF…

C++ 11 【可变参数模板】【lambda】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 一、新的类功能 1.1默认成员函数—…

【数据分析基础】实验三 文件操作、数组与矩阵运算

一&#xff0e;实验目的 掌握上下文管理语句with的使用方法。掌握文本文件的操作方法。了解os、os.path模块的使用。掌握扩展库Python-docx、openpyxl的安装与操作word、Excel文件内容的方法。熟练掌握numpy数组相关运算和简单应用。熟练使用numpy创建矩阵&#xff0c;熟悉常用…

新技术前沿-2024-构建个人知识库和小语言模型

OllamaWebUIAnythingLLM&#xff0c;构建安全可靠的个人/企业知识库 1 技术路线一 1.1 搭建本地大模型Ollama 1.2 搭建用户界面open WebUI 使用Docker Desktop Open-webui。它可以快速基于Ollama构筑本地UI。 如果没有科学上网&#xff0c;很可能会拉不动&#xff0c;可以试…

linux网络服务“PXE网络批量装机和Kickstart全自动化安装”

PXE网络批量装机 pxe自动装机&#xff1a; 服务端和客户端 pxe c/s 模式&#xff1a;允许客户端通过网络从远程服务器&#xff08;服务端&#xff09;下载引导镜像&#xff0c;加载安装文件&#xff0c;实现自动化安装操作系统。 无人值守 :安装选项不需要人为干预&#xf…

FlexJavaFramwork

FlexJavaFramwork架构

【高校科研前沿】广西大学博士生冯德东为一作在Habitat Int发文:区域乡村性与贫困治理变化的时空格局及相关效应——以滇桂黔石漠化地区为例

1.文章简介 论文名称&#xff1a;Spatio-temporal patterns and correlation effects of regional rurality and poverty governance change: A case study of the rocky desertification area of Yunnan-Guangxi-Guizhou, China&#xff08;区域乡村性与贫困治理变化的时空格…

从零开始搭建Electron项目(二)之例程解析

本专栏&#xff0c;前面学习了怎么下载例程并运行。 这里解析例程的构成 从零开始搭建Electron项目之运行例程-CSDN博客文章浏览阅读22次。最好的学习方式就是&#xff1a;给一段能够运行的代码示例。本文给出了例程资源&#xff0c;以及运行的步骤。在国内开发electron有一点特…

【吊打面试官系列-Mysql面试题】MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 ?

大家好&#xff0c;我是锋哥。今天分享关于 【MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f; 以下是 MySQL_fetch_array 和 MySQL…