作者:小傅哥
博客: https://bugstack.cn - 原创系列专题文章
沉淀、分享、成长,让自己和他人都能有所收获!:smile:
持之以恒的重要性
初学编程往往都很懵,几乎在学习的过程中会遇到各种各样的问题,哪怕别人那运行好好的代码,但你照着写完就报错。但好在你坚持住了,否则你可能看不到这篇文章。时间和成长就是相互关联着,你在哪条路上坚持走的久,就能看见那条的终点有多美,但如果你浪费了一次又一次努力的机会,那么你也会同样错过很多机遇,因为你的路换了。坚持学习、努力成长,持以恒的付出一定会有所收获。
学习方法的重要性
不会学习往往会耽误很多时间,又没有可观的收成。但不会学习有时候是因为 懒 造成的,尤其是学习视频、书籍资料、技术文档等,如果只是看了却不是实际操作验证,那么真的很难把别人的知识让自己吸收,即使是当时感觉会了也很快就会忘记。时而也经常会有人找到你说;“这个我不知道,你先告诉我,过后我就学。”但过后你学了吗?
你愿意为一个知识盲区付出多长时间
你心里时而会蹦出这样的词吗; 太难了我不会
、 找个人帮一下吧
、 放弃了放弃了
,其实谁都可能遇到很不好解决的问题,也是可以去问去咨询的。但,如果在这之前你没有在自己的大脑中反复的寻找答案,那么你的大脑中就不会形成一个凸点的知识树,缺少了这个学习过程也就缺少了查阅各种资料给自己大脑填充知识的机会,哪怕是问到了答案最终也会因时间流逝而忘记。
bugstack虫洞栈
,回复 源码下载
获取(打开获取的链接,找到序号18) 工程 | 描述 |
---|---|
itstack-demo-design-14-01 | 使用一坨代码实现业务需求 |
itstack-demo-design-14-02 | 通过设计模式优化代码结构,增加扩展性和维护性 |
命令模式在我们通常的互联网开发中相对来说用的比较少,但这样的模式在我们的日常中却经常使用到,那就是 Ctrl+C
、 Ctrl+V
。当然如果你开发过一些桌面应用,也会感受到这样设计模式的应用场景。从这样的模式感受上,可以想到这是把逻辑实现与操作请求进行分离,降低耦合方便扩展。
命令模式是行为模式中的一种,以数据驱动的方式将 命令对象
,可以使用构造函数的方式传递给调用者。调用者再提供相应的实现为命令执行提供操作方法。可能会感觉这部分有一些饶,可以通过对代码的实现进行理解,在通过实操来熟练。
在这个设计模式的实现过程中有如下几个比较重要的点;
命令场景的核心的逻辑是调用方与不需要去关心具体的逻辑实现,在这个场景中也就是点餐人员只需要把需要点的各种菜系交个 小二
就可以,小二再把各项菜品交给各个厨师进行烹饪。也就是点餐人员不需要跟各个厨师交流,只需要在统一的环境里下达命令就可以。
在这个场景中可以看到有不同的菜品;山东(鲁菜)、四川(川菜)、江苏(苏菜)、广东(粤菜)、福建(闽菜)、浙江(浙菜)、湖南(湘菜),每种菜品都会有不同的厨师:woman::egg:进行烹饪。而客户并不会去关心具体是谁烹饪,厨师也不会去关心谁点的餐。客户只关心早点上菜,厨师只关心还有多少个菜要做。而这中间的衔接的过程,由小二完成。
那么在这样的一个模拟场景下,可以先思考 哪部分是命令模式的拆解,哪部分是命令的调用者以及命令的实现逻辑。
不考虑设计模式的情况下,在做这样一个点单系统,有一个类就够了
像是这样一个复杂的场景,如果不知道设计模式直接开发,也是可以达到目的的。但对于后续的各项的菜品扩展、厨师实现以及如何调用上会变得非常耦合难以扩展。
itstack-demo-design-14-01 └── src └── main └── java └── org.itstack.demo.design └── XiaoEr.java
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()
。 接下来使用命令模式来进行代码优化,也算是一次很小的重构。
命令模式可以将上述的模式拆解三层大块,命令、命令实现者、命令的调用者,当有新的菜品或者厨师扩充时候就可以在指定的类结构下进行实现添加即可,外部的调用也会非常的容易扩展。
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
/** * 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获! * 公众号:bugstack虫洞栈 * Create by 小傅哥(fustack) @2020 * * 菜系 * 01、山东(鲁菜)——宫廷最大菜系,以孔府风味为龙头。 * 02、四川(川菜)——中国最有特色的菜系,也是民间最大菜系。 * 03、江苏(苏菜)——宫廷第二大菜系,古今国宴上最受人欢迎的菜系。 * 04、广东(粤菜)——国内民间第二大菜系,国外最有影响力的中国菜系,可以代表中国。 * 05、福建(闽菜)——客家菜的代表菜系。 * 06、浙江(浙菜)——中国最古老的菜系之一,宫廷第三大菜系。 * 07、湖南(湘菜)——民间第三大菜系。 * 08、安徽(徽菜)——徽州文化的典型代表。 */ public interface ICuisine { void cook(); // 烹调、制作 }
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()
。 public interface ICook { void doCooking(); }
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("四川厨师,烹饪川菜,中国最有特色的菜系,也是民间最大菜系。"); } }
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(); } }
@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);
,这里分别添加了四种菜品,给小二。 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
命令
、 实现
、 调用者
,而这三块内容的拆分也是选择适合场景的关键因素,经过这样的拆分可以让逻辑具备单一职责的性质,便于扩展。