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

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]"

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FSQ8罗德与施瓦茨FSQ8信号分析仪

181/2461/8938产品概述&#xff1a; R&S FSQ8 信号分析仪将两种仪器合二为一。它提供高达 120 MHz 解调带宽的信号分析以及高端频谱分析仪的动态范围。 频率范围&#xff1a;20 Hz 至 8 GHz高端频谱分析仪的动态范围 TOI 25 dBm&#xff0c;典型值1 dB 压缩 13 dBm&…

【已解决】HalconDotNet.HOperatorException:“HALCON error #1201: Wrong type of control

前言 最近在学习Halcon视觉和C#的联合开发&#xff0c;碰到一个比较有意思的问题记录一下&#xff0c;大致的报错信息是说我用的halcondotnet版本和我在halcon导出的使用的halcondotnet.dll版本不一致&#xff0c;所以才报错的&#xff01; 解决 首先你得找到你安装halcon的…

安全测试概述和用例设计

一、安全测试概述 定义&#xff1a;安全测试是在软件产品开发基本完成时&#xff0c;验证产品是否符合安全需求定义和产品质量标准的过程。 概念&#xff1a;安全测试是检查系统对非法侵入渗透的防范能力。 准则&#xff1a;理论上来讲&#xff0c;只要有足够的时间和资源&a…

哈希字典Dict

目录 Dict的组成 1.hash表的节点 2.hash表结构 3.字典 4.Dict结构 hash算法 哈希函数 什么情况下使用 rehash rehash主要的函数dictExpand 怎么判断要进行扩容还是收缩 什么时候会用到扩展和收缩 渐进式rehash 渐进式rehash主要的函数dictRehash 字典API 字典的…

shopee虾皮运营技巧,学会这几招能有效提高销量

在电商的运营过程中&#xff0c;新手总会踩上不少的坑&#xff0c;运气好的累计成经验值&#xff0c;应用于日后的店铺运营中&#xff0c;运气不好的&#xff0c;进坑了就再也爬不上来了&#xff0c;特别是跨境电商&#xff0c;本身与国内电商存在很大的信息差&#xff0c;不可…

【EI会议】2024年机械制造、航天工程技术与材料应用国际学术会议 (MMAETMA 2024)

2024 International Academic Conference on Mechanical Manufacturing, Aerospace Engineering Technology and Materials Application (MMAETMA 2024) 会议地点&#xff1a;长沙&#xff0c;中国 截稿时间&#xff1a;2024.4.30 网址&#xff1a;http://www.mmaetma.com/ …

Linux环境下安装redis踩坑zmalloc.h:50:31收录(附解决方法)

老将回归第一篇问题收录&#xff08;苦笑&#xff09; 报错收录 解决&#xff1a;zmalloc.h:50:31: 致命错误&#xff1a;jemalloc/jemalloc.h&#xff1a;没有文件或目录 解法一&#xff1a; # 执行如下命令 MALLOC环境变量去建立Redis的libcmake MALLOClibc解决 解法二&a…

FJSP:霸王龙优化算法(Tyrannosaurus optimization,TROA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

一、柔性作业车间调度问题 柔性作业车间调度问题&#xff08;Flexible Job Shop Scheduling Problem&#xff0c;FJSP&#xff09;&#xff0c;是一种经典的组合优化问题。在FJSP问题中&#xff0c;有多个作业需要在多个机器上进行加工&#xff0c;每个作业由一系列工序组成&a…

企业IT运维事中故障定位方法及工具

企业IT故障定位指诊断故障直接原因或根因&#xff0c;故障定位有助于故障恢复动作更加有效。故障定位通常是整个故障过程中耗时最长的环节&#xff0c;定位的目标围绕在快速恢复的基础上&#xff0c;而非寻找问题根因&#xff0c;后者由问题管理负责。通常大部分可用性故障&…

电脑桌面监控软件

电脑桌面监控软件 有一项触目精心的统计&#xff0c;2022年上半年&#xff0c;有33.2%的网民在单位上网&#xff0c;上网行为包括聊天、游戏、看小说、炒股等等&#xff0c;可以说五花八门。这些行为给企业带来四个问题&#xff1a; &#xff08;1&#xff09;宽带资源被滥用。…

Myelsa的Python函数之旅(高铁直达)

一、函数的定义&#xff1a; 函数(Function)是一段可重复使用的代码块&#xff0c;用于执行特定的任务或计算&#xff0c;并可以接受输入参数和返回输出结果。函数可以将复杂的问题分解为更小的子问题&#xff0c;提高代码的可读性和可维护性。 二、函数的组成&#xff1a; 在…

面试:HashMap

目录 1、底层数据结构&#xff0c;1.7 与1.8有何不同? 2、为何要用红黑树&#xff0c;为何一上来不树化&#xff0c;树化阈值为何是8&#xff0c;何时会树化&#xff0c;何时会退化为链表? 3、索引如何计算? hashCode都有了&#xff0c;为何还要提供hash()方法?数组容量为…

java实现运行脚本文件

在最近的项目中&#xff0c;有一个需求是前端传给我一个脚本文件&#xff0c;然后我需要运行脚本文件后将结果进行返回&#xff0c;那接下来就让我们看看是怎么做的吧&#xff01; public R runScripts(Integer id) {ScriptsInfo scriptsInfo this.baseMapper.selectById(id);…

【数据结构】红黑树详解

目录 前言&#xff1a; 红黑树的概念&#xff1a; 红黑树的性质: 红黑树节点的定义&#xff1a; 红黑树的插入&#xff1a; 情况1&#xff1a;cur为红&#xff0c;p为红&#xff0c;g为黑&#xff0c;u存在且为红 情况2&#xff1a;cur为红&#xff0c;p为红&#xff0c…

CY5.5-NH2生物分子荧光标记Cy5.5 星戈瑞

CY5.5-NH2是一种常用的生物分子荧光标记染料&#xff0c;属于Cy5.5系列染料的一种。它具有强烈的荧光信号和较高的光稳定性&#xff0c;因此在生物分子标记和成像领域得到应用。 CY5.5-NH2染料具有一个氨基(-NH2)官能团&#xff0c;可以与生物分子中的羧基(-COOH)或其他活性基…

linux网络预备

网络预备 网络协议初识 协议分层 打电话例子 在这个例子中, 我们的协议只有两层; 但是实际的网络通信会更加复杂, 需要分更多的层次。 分层最大的好处在于 “封装” 。 OSI七层模型 OSI&#xff08;Open System Interconnection&#xff0c;开放系统互连&#xff09;七层网…

用Python做一个4399游戏脚本原来这么简单 !(内含完整思路)

说明 简述&#xff1a;本文将以4399小游戏《宠物连连看经典版2》作为测试案例&#xff0c;通过识别小图标&#xff0c;模拟鼠标点击&#xff0c;快速完成配对。对于有兴趣学习游戏脚本的同学有一定的帮助。 运行环境&#xff1a;Win10/Python3.5。 主要模块&#xff1a;win3…

汇编语言:寻址方式在结构化数据访问中的应用——计算人均收入

有一年多没有在CSDN上发博文了。人的工作重心总是有转移的&#xff0c;庆幸一直在做着有意义的事。   今天的内容&#xff0c;是为汇编语言课程更新一个实验项目。      本方案修改自王爽编《汇编语言》第&#xff14;版P172“实验7寻址方式在结构化数据访问中的应用” …

nodejs应用程序以守护进程daemon的方式启动,容器化部署的时候一直部署出错,导致无法成功启动程序。

一、背景 nodejs应用程序使用Egg.js 框架脚本命令&#xff0c;见package.json&#xff1a; "scripts": {"debug": "egg-bin debug","clean": "easy clean","build": "easy build prod","start&…

数字逻辑分析仪初体验

为啥会用到这玩意儿&#xff0c;要从一个荒诞的需求开始。想在市面上找一款特别低空飞行的监控&#xff0c;而且不想它一直开着监控&#xff0c;最好是我在外面远程指挥它起飞&#xff0c;飞去厨房&#xff0c;飞去洗手间&#xff0c;甚至飞去阳台&#xff0c;查看水龙头情况啊…