烤羊肉串引来的思考--命令模式

1.1 吃羊肉串!

        烧烤摊旁边等着拿肉串的人七嘴八舌地叫开了。场面有些混乱,由于人实在太多,烤羊肉串的老板已经分不清谁是谁,造成分发错误,收钱错误,烤肉质量不过关等。

        外面打游击烤羊肉串和这种开门店做烤肉,哪个更赚钱?

        这很难讲,毕竟各有各的好,在外面打游击,好处是不用租房,不用上税,最多就是交点'保护费',但下雨天不行、大白天不行、太晚也不行,一般都是傍晚做几个钟头,顾客也不固定,像刚才那个,由于人多造成混乱,于是就放跑了我们这两条大鱼,其实他的生意是不稳定的。大白天,城管没下班呢,怎能容忍他如此安逸。超过晚上11点,夜深人静,谁还愿意站在路边吃烤肉。但开门店就不一样了,不管什么时间都可以做生意,由于环境相对好,所以固定客户就多,看似好像房租交出去了,但其实由于顾客多,而且是正经做生意,所以最终可以赚到大钱。

1.2 烧烤摊vs.烧烤店

        "因为要吃烤肉的人太多,都希望能最快吃到肉串,烤肉老板一个人,所以有些混乱。"
        "还不止这些,老板一个人,来的人一多,他就未必记得住谁交没交过钱,要几串,需不需要放辣等。"
        "是呀,大家都站在那里,没什么事,于是都盯着烤肉去了,哪一串多、哪一串少、哪一串烤得好、哪一串烤得焦看得清清楚楚,于是'挑剔'也就接踵而至。"
        "这其实就是我们在编程中常说的什么?"
        "我想想,你是想说'紧耦合'?"
        "哈,不错,不枉我的精心栽培。"
        "由于客户和烤羊肉串老板的'紧耦合',所以容易出错,也容易产生挑剔。"
        "说得对,这其实就是'行为请求者'与'行为实现者'的紧耦合。我们需要记录哪个人要几串羊肉串,有没有特殊要求(放辣不放辣),付没付过钱,谁先谁后,这其实都相当于对请求做什么?"
        "对请求做记录,啊,应该是做日志。"
        "很好,那么如果有人需要退回请求,或者要求烤肉重烤,这其实就是?"
        "就相当于撤销和重做吧。"
        "OK,所以对请求排队或记录请求日志,以及支持可撤销的操作等行为时,'行为请求者'与'行为实现者'的紧耦合是不太适合的。你说怎么办?"
        "哈,这是最终结果,不是这个意思,我们是烤肉请求者,烤肉的师傅是烤肉的实现者,对于开门店来说,我们用得着去看着烤肉的实现过程吗?现实是怎么做的呢?"
        "哦,我明白你的意思了,我们不用去认识烤肉者是谁,连他的面都不用见到,我们只需要给接待我们的服务员说我们要什么就可以了。他可以记录我们的请求,然后再由他去通知烤肉师傅做。"

        "而且,由于我们所做的请求,其实也就是我们点肉的订单,上面有很详细的我们的要求,所有的客户都有这一份订单,烤肉师傅可以按先后顺序操作,不会混乱,也不会遗忘了。"
        "收钱的时候,也不会多收或少收。"
        "优点还不止这里,比如说,""服务员,我们那十串羊肉串太多了,改成六串就可以了。"
        "好的!"服务员答道。
        "你注意看他接着做了什么?"
        "他好像在一个小本子上划了一下,然后去通知烤肉师傅了。"
        "这其实是在做撤销行为的操作。由于有了记录,所以最终算账是不会错的。"
        "对对对,这种利用一个服务员来解耦客户和烤肉师傅的处理好处真的很多。"

1.3 紧耦合设计

代码结构图

package code.chapter23.command1;public class Test {public static void main(String[] args) {System.out.println("**********************************************");       System.out.println("《大话设计模式》代码样例");System.out.println();       Barbecuer boy = new Barbecuer();boy.bakeMutton();boy.bakeMutton();boy.bakeMutton();boy.bakeChickenWing();boy.bakeMutton();boy.bakeMutton();boy.bakeChickenWing();System.out.println();System.out.println("**********************************************");}
}//烤肉串者
class Barbecuer{//烤羊肉public void bakeMutton(){System.out.println("烤羊肉串!");}//烤鸡翅public void bakeChickenWing(){System.out.println("烤鸡翅!");}
}

        "很好,这就是路边烤肉的对应,如果用户多了,请求多了,就容易乱了。那你再尝试用门店的方式来实现它。"
        "我知道一定需要增加服务员类,但怎么做有些不明白。"
        "嗯,这里的确是难点,要知道,不管是烤羊肉串,还是烤鸡翅,还是其他烧烤,这些都是'烤肉串者类'的行为,也就是他的方法,具体怎么做都是由方法内部来实现,我们不用去管它。但是对于'服务员'类来说,他其实就是根据用户的需要,发个命令,说:'有人要十个羊肉串,有人要两个鸡翅',这些都是命令……"
        "我明白了,你的意思是,把'烤肉串者'类当中的方法,分别写成多个命令类,那么它们就可以被'服务员'来请求了?"
        "是的,说得没错,这些命令其实差不多都是同一个样式,于是你就可以泛化出一个抽象类,让'服务员'只管对抽象的'命令'发号施令就可以了。具体是什么命令,即烤什么,由客户来决定吧。"

1.4 命令模式

        命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。[DP]
命令模式(Command)结构图

package code.chapter23.command0;import java.util.ArrayList;
import java.util.Date;
import java.text.SimpleDateFormat;public class Test {public static void main(String[] args) {System.out.println("**********************************************");       System.out.println("《大话设计模式》代码样例");System.out.println(); Receiver receiver = new Receiver();Command command = new ConcreteCommand(receiver);Invoker invoker = new Invoker();invoker.setCommand(command);invoker.executeCommand();System.out.println();System.out.println("**********************************************");}
}//抽象命令类
abstract class Command {protected Receiver receiver;public Command(Receiver receiver){this.receiver = receiver;}//执行命令public abstract void excuteCommand();
}//具体命令类
class ConcreteCommand extends Command{public ConcreteCommand(Receiver receiver){super(receiver);}public void excuteCommand(){receiver.action();}
}class Invoker{private Command command;public void setCommand(Command command){this.command = command;}public void executeCommand(){command.excuteCommand();}}class Receiver{public void action(){System.out.println("执行请求!");}
}


Command类,用来声明执行操作的接口。
ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现executeCommand。
Invoker类,要求该命令执行这个请求。
Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。
客户端代码,创建一个具体命令对象并设定它的接收者。

1.5 松耦合设计

代码结构图

package code.chapter23.command2;public class Test {public static void main(String[] args) {System.out.println("**********************************************");       System.out.println("《大话设计模式》代码样例");System.out.println();       //开店前的准备Barbecuer boy = new Barbecuer();//烤肉厨师Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);            //烤羊肉串Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);  //烤鸡翅Waiter girl = new Waiter();     //服务员//开门营业girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.notifyCommand();                   //通知厨师烤肉girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.notifyCommand();                   //通知厨师烤肉girl.setOrder(bakeChickenWingCommand1); //下单烤鸡翅girl.notifyCommand();                   //通知厨师烤肉System.out.println();System.out.println("**********************************************");}
}//抽象命令类
abstract class Command {protected Barbecuer receiver;public Command(Barbecuer receiver){this.receiver = receiver;}//执行命令public abstract void excuteCommand();
}//烤羊肉命令类
class BakeMuttonCommand extends Command{public BakeMuttonCommand(Barbecuer receiver){super(receiver);}public void excuteCommand(){receiver.bakeMutton();}
}//烤鸡翅命令类
class BakeChickenWingCommand extends Command{public BakeChickenWingCommand(Barbecuer receiver){super(receiver);}public void excuteCommand(){receiver.bakeChickenWing();}
}//服务员类
class Waiter{private Command command;//设置订单public void setOrder(Command command){this.command = command;}//通知执行public void notifyCommand(){command.excuteCommand();}
}//烤肉串者
class Barbecuer{//烤羊肉public void bakeMutton(){System.out.println("烤羊肉串!");}//烤鸡翅public void bakeChickenWing(){System.out.println("烤鸡翅!");}
}

        "很好很好,基本都把代码实现了。但没有体现出命令模式的作用。比如下面几个问题:第一,真实的情况其实并不是用户点一个菜,服务员就通知厨房去做一个,那样不科学,应该是点完烧烤后,服务员一次通知制作;第二,如果此时鸡翅没了,不应该是客户来判断是否还有,客户哪知道有没有呀,应该是服务员或烤肉串者来否决这个请求;第三,客户到底点了哪些烧烤或饮料,这是需要记录日志的,以备收费,也包括后期的统计;第四,客户完全有可能因为点的肉串太多而考虑取消一些还没有制作的肉串。这些问题都需要得到解决。"
        "这,这怎么办到呀?"
        "重构一下服务员Waiter类,尝试改一下。将private Command command;改成一个ArrayList,就能解决了。"

1.6 进一步改进命令模式

package code.chapter23.command3;import java.util.ArrayList;
import java.util.Date;
import java.text.SimpleDateFormat;public class Test {public static void main(String[] args) {System.out.println("**********************************************");       System.out.println("《大话设计模式》代码样例");System.out.println();       //开店前的准备Barbecuer boy = new Barbecuer();//烤肉厨师Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);            //烤羊肉串Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);  //烤鸡翅Waiter girl = new Waiter();     //服务员System.out.println("开门营业,顾客点菜");girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.setOrder(bakeMuttonCommand1);      //下单烤羊肉串girl.cancelOrder(bakeMuttonCommand1);   //取消一串羊肉串订单girl.setOrder(bakeChickenWingCommand1); //下单烤鸡翅System.out.println("点菜完毕,通知厨房烧菜");girl.notifyCommand();                   //通知厨师System.out.println();System.out.println("**********************************************");}
}//抽象命令类
abstract class Command {protected Barbecuer receiver;public Command(Barbecuer receiver){this.receiver = receiver;}//执行命令public abstract void excuteCommand();
}//烤羊肉命令类
class BakeMuttonCommand extends Command{public BakeMuttonCommand(Barbecuer receiver){super(receiver);}public void excuteCommand(){receiver.bakeMutton();}
}//烤鸡翅命令类
class BakeChickenWingCommand extends Command{public BakeChickenWingCommand(Barbecuer receiver){super(receiver);}public void excuteCommand(){receiver.bakeChickenWing();}
}//服务员类
class Waiter{private ArrayList<Command> orders = new ArrayList<Command>();//设置订单public void setOrder(Command command){String className=command.getClass().getSimpleName();if (className.equals("BakeChickenWingCommand")){System.out.println("服务员:鸡翅没有了,请点别的烧烤。");}else{this.orders.add(command);System.out.println("增加订单:"+className+" 时间:"+getNowTime());}}//取消订单public void cancelOrder(Command command){String className=command.getClass().getSimpleName();orders.remove(command);System.out.println("取消订单:"+className+" 时间:"+getNowTime());}//通知执行public void notifyCommand(){for(Command command : orders)command.excuteCommand();}private String getNowTime(){SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");return formatter.format(new Date()).toString();}
}//烤肉串者
class Barbecuer{//烤羊肉public void bakeMutton(){System.out.println("烤羊肉串!");}//烤鸡翅public void bakeChickenWing(){System.out.println("烤鸡翅!");}
}

1.7 命令模式的作用

        "我觉得第一,它能较容易地设计一个命令队列;第二,在需要的情况下,可以较容易地将命令记入日志;第三,允许接收请求的一方决定是否要否决请求。"
"还有就是第四,可以容易地实现对请求的撤销和重做;第五,由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。其实还有最关键的优点就是命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。[DP]
        "但是否是碰到类似情况就一定要实现命令模式呢?"
        "这就不一定了,比如命令模式支持撤销/恢复操作功能,但你还不清楚是否需要这个功能时,你要不要实现命令模式?"
        "要,万一以后需要就不好办了。"
        "其实应该是不要实现。敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。[R2P]"

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

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

相关文章

React - 你知道在React组件的哪个阶段发送Ajax最合适吗

难度级别:中级及以上 提问概率:65% 如果求职者被问到了这个问题,那么只是单纯的回答在哪个阶段发送Ajax请求恐怕是不够全面的。最好是先详细描述React组件都有哪些生命周期,最后再回过头来点题作答,为什么应该在这个阶段发送Ajax请求。那…

7.1.4 Selenium 爬取京东商品信息实战

目录 1、实战内容 2、思路 3、分析 url 4、开始操作 1、得到 Cookies 2、访问页面&#xff0c;得到 response 3、解析页面 4、存入 MySQL 5、1-3步总代码 1、实战内容 爬取京东笔记本电脑商品的信息(如&#xff1a;价格、商品名、评论数量)&#xff0c;存入 MySQL 中…

JSBridge原理 - 前端H5与客户端Native交互

1. 概述&#xff1a; 在混合应用开发中&#xff0c;一种常见且成熟的技术方案是将原生应用与 WebView 结合&#xff0c;使得复杂的业务逻辑可以通过网页技术实现。实现这种类型的混合应用时&#xff0c;就需要解决H5与Native之间的双向通信。JSBridge 是一种在混合应用中实现 …

uni-app(H5)论坛 | 社区 表情选择 UI组件

项目源码请移步&#xff1a;bbs 效果 实现思路 表情切换 人物、动物、小黄人不同表情之间的切换实际就是组件的切换 emoji表情 emoji表情本身就是一种字符 如需其他emoji表情可参考 EmojiAll中文官方网站 需要注意的就是数据库的存储格式需要支持emoji表情&#xff0c;我项…

使用Python将多张图片转换为动态GIF图像

在本文中&#xff0c;我们将学习如何使用Python编写代码&#xff0c;将多张静态图片转换为一个动态的GIF图像。无论你的图片格式是JPEG&#xff08;.jpg&#xff09;还是PNG&#xff08;.png&#xff09;&#xff0c;我们都将使用Python中的PIL库来实现这一功能。通过本文的学习…

证书生成和获取阿里云备案获取密钥流程

1.在java文件夹下 输入 cmd 打开命令行窗口 2. keytool -genkey -alias 证书名 -keyalg RSA -keysize 2048 -validity 36500 -keystore 证书名.keystore 输入这一行&#xff0c;把证书名三个字 改成 项目的名称&#xff08;例如&#xff1a;D23102802&#xff09; 3. 密码默认填…

【HTML】简单制作一个分形动画

目录 前言 开始 HTML部分 效果图 ​编辑​编辑​编辑​编辑总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;其中HTML的文件名改为[index.html]&a…

金融企业区域集中库的设计构想和测试验证

导读 本文探讨了金融企业区域集中库的设计构想和测试验证&#xff0c;包括架构设想、数据库整合场景测试及优势和使用设想。作者提出利用 TiDB 数据库产品集中建设区域集中库&#xff0c;解决 MySQL 存量节点的整合问题&#xff0c;实现部署的标准化、按需扩展和统一运维管理。…

gitlab使用

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 目录 一&#xff1a…

SSL中的CA证书

目录 一、CA概述 二、数据加密 三、身份认证 一、CA概述 SSL如何保证网络通信的安全和数据的完整性呢&#xff1f;就是采用了两种手段&#xff1a;身份认证和数据加密。身份认证就需要用到CA证书。 CA是证书的签发机构&#xff0c;它是公钥基础设施&#xff08;Public Key In…

鸿蒙OS开发实战:【自动化测试框架】使用指南

概述 为支撑HarmonyOS操作系统的自动化测试活动开展&#xff0c;我们提供了支持JS/TS语言的单元及UI测试框架&#xff0c;支持开发者针对应用接口进行单元测试&#xff0c;并且可基于UI操作进行UI自动化脚本的编写。 本指南重点介绍自动化测试框架的主要功能&#xff0c;同时…

linux centos 系统 docker及podman拉取kylin麒麟镜像内部及部署安装Gaussdb数据库

研究总结来之不易 1.首先下载安装包&#xff0c;网址&#xff1a; 软件包 | openGauss 2.参考安装连接&#xff1a; 单节点安装 openGauss学习笔记-03 openGauss极简版单节点安装_opengauss 笔记-CSDN博客 当然他们说的有些也是不完全一样的&#xff0c;根据自己的环境摸索…

.NET 设计模式—装饰器模式(Decorator Pattern)

简介 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你在不改变对象接口的前提下&#xff0c;动态地将新行为附加到对象上。这种模式是通过创建一个包装&#xff08;或装饰&#xff09;对象&#xff0c;将要被装饰的对象包裹起来…

设计模式之命令模式讲解

概念&#xff1a;命令模式&#xff08;Command Pattern&#xff09;又称行动&#xff08;Action&#xff09;模式或交易&#xff08;Transaction&#xff09;模式。将一个请求封装成一个对象&#xff0c;从而让你使用不同的请求把客户端参数化&#xff0c;对请求排队或者记录请…

2月智能手表线上电商市场(京东天猫淘宝)分析:华为手表成最大赢家!

近年来&#xff0c;各大厂商纷纷积极布局健康管理领域&#xff0c;智能手表成为可穿戴市场的热门产品。随着越来越多的厂商进入&#xff0c;智能手表的芯片技术、显示屏技术、传感器技术等都在不断进步&#xff0c;整体性能和功能得到显著提升&#xff0c;使得用户体验更加出色…

【Labview】虚拟仪器技术

一、背景知识 1.1 虚拟仪器的定义、组成和应用 虚拟仪器的特点 虚拟仪器的突出特征为“硬件功能软件化”&#xff0c;虚拟仪器是在计算机上显示仪器面板&#xff0c;将硬件电路完成信号调理和处理功能由计算机程序完成。 虚拟仪器的组成 硬件软件 硬件是基础&#xff0c;负责将…

0104练习与思考题-算法基础-算法导论第三版

2.3-1 归并示意图 问题&#xff1a;使用图2-4作为模型&#xff0c;说明归并排序再数组 A ( 3 , 41 , 52 , 26 , 38 , 57 , 9 , 49 ) A(3,41,52,26,38,57,9,49) A(3,41,52,26,38,57,9,49)上的操作。图示&#xff1a; tips:&#xff1a;有不少在线算法可视化工具&#xff08;软…

基于taro搭建小程序多项目框架

前言 为什么需要这样一个框架&#xff0c;以及这个框架带来的好处是什么&#xff1f; 从字面意思上理解&#xff1a;该框架可以用来同时管理多个小程序&#xff0c;并且可以抽离公用组件或业务逻辑供各个小程序使用。当你工作中面临这种同时维护多个小程序的业务场景时&#…

【Mysql高可用集群-双主双活-myql+keeplived】

Mysql高可用集群-双主双活-myqlkeeplived 一、介绍二、准备工作1.两台centos7 linux服务器2.mysql安装包3.keepalived安装包 三、安装mysql1.在128、129两台服务器根据《linux安装mysql服务-两种安装方式教程》按方式一安装好mysql应用。2.修改128服务器/etc/my.cnf配置文件&am…

mynet开源库

1.介绍 个人实现的c开源网络库&#xff0e; 2.软件架构 1.结构图 2.基于event的自动分发机制 3.多优先级分发队列&#xff0c;延迟分发队列 内部event服务于通知机制的优先级为0&#xff0c;外部event优先级为1&#xff0e; 当集中处理分发的event_callback时&#xff0c…