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

news2024/11/26 19:24:02

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.coloradmin.cn/o/1578422.html

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

相关文章

Windows系统下安装java开发环境所需的JDK开发工具包

目录 一、JDK开发工具包下载二、安装三、环境变量配置3.1 添加安装包路径3.2 添加lib路径3.3 添加bin目录 四、检查是否安装成功五、总结 一、JDK开发工具包下载 官网地址&#xff1a;JDK下载 打开网址后有多个版本的JDK&#xff0c;学者根据自己电脑需求选择对应版本下载。如…

类,构造,this,static

第1关&#xff1a;什么是类&#xff0c;如何创建类 100 任务要求参考答案 任务描述相关知识 什么是类怎么定义类创建对象并且使用对象的属性和方法编程要求测试说明 任务描述 本关任务&#xff1a;创建一个类和一个对象&#xff0c;调用这个对象的属性和方法。 相关知识 …

34-5 CSRF漏洞 - CSRF分类

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 1)GET 类型 传参: 参数连接在URL后面 POC构造及执行流程: 构造URL,诱导受害者访问点击利用利用标签进行攻击: 构造虚假URL,在链接上添加payload抓包获取数据包,通过CSRF POC…

解决Xshell连接Linux虚拟机速度慢问题

我们频繁更换网络环境时&#xff0c;可能会发现xshell连接Linux虚拟机的速度变得很慢 为什么呢&#xff1f; 因为ssh的服务端在连接时会自动检测dns环境是否一致导致的 我们把它修改为不检测即可 修改文件位置&#xff1a; vi /etc/ssh/sshd_config 把 #UseDNS yes 修改…

Redis7(二)数据类型及其用法

一、概述 命令不区分大小写&#xff0c;key区分大小写 数据类型针对value String List Set Hash ZSet bitmap GEO HyperLogLog Stream bitfield 二、String <K,V> 1、设值/取值 getrange key index1 index2 getrange key 0 -1//获取所有的值 SETRANGE KEY_N…

代码随想录第34天| 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

1005.K次取反后最大化的数组和 1005. K 次取反后最大化的数组和 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 贪心算法&#xff0c;这不就是常识&#xff1f;还能叫贪心&#xff1f;LeetCode&#xff1a;1005.K次取反后最大化的数组和_哔哩哔…

如何选择苹果iOS系统的企业签名分发平台

哈喽&#xff0c;大家好呀&#xff0c;淼淼有和大家见面啦&#xff0c;前两期讲了分发内测的一些相关知识&#xff0c;这一期咱们来聊聊企业签名分发平台的相关知识。最近移动应用市场的竞争一天比一天要激烈&#xff0c;许多做开发的小伙伴们都在为此发愁&#xff0c;愁着该怎…

不要再使用 @Builder 注解了!有深坑呀!

曾经&#xff0c;我在《千万不要再随便使用 lombok 的 Builder 了&#xff01;》 一文中提到 Builder 注解的其中一个大坑会导致默认值失效&#xff01; 最近阅读了 《Oh !! Stop using Builder》 发现 Builder 的问题还不止一个&#xff0c;Builder 会让人误以为是遵循构建器…

java的封装

在Java中&#xff0c;封装是面向对象编程中的一种重要概念&#xff0c;它指的是将数据和方法打包在一个单一的单位&#xff08;类&#xff09;中&#xff0c;并对外部隐藏对象的内部细节。封装通过将类的成员变量声明为私有的&#xff0c;并提供公共的方法来访问和修改这些变量…

数据库关系模式三元及以上分解无损连接判断(表格法)

例题 1.首先构造初始表&#xff0c;如下表所示。 A B C D E ABC a1 a2 a3 b14 b15 CD b21 b22 a3 a4 b15 DE b31 b32 b33 a4 a5 2.遍历函数依赖&#xff0c;对AB→C&#xff0c;因各元组的第一、二列没有相同的分量&#xff0c;所以表不改变。 3.由C→D…

【日常记录】【JS】getComputedStyle 获取DOM的最终样式值

文章目录 1、介绍2、getComputedStyle3、链接 1、介绍 Window.getComputedStyle()方法返回一个对象&#xff0c;该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有 CSS 属性的值。私有的 CSS 属性值可以通过对象提供的 API 或通过简单地使用 CSS 属性…

指针的深入理解(六)

指针的深入理解&#xff08;六&#xff09; 个人主页&#xff1a;大白的编程日记 感谢遇见&#xff0c;我们一起学习进步&#xff01; 文章目录 指针的深入理解&#xff08;六&#xff09;前言一. sizeof和strlen1.1sizeof1.2strlen1.3sizeof和strlen对比 二.数组名和指针加减…

javaWeb物流信息网的设计与实现

摘要 本文讲述了基于JSP物流信息网的设计与实现。该系统使用java语言开发&#xff0c;使系统具有更好的平台性和可扩展性。 该系统实现了用户登录、注册、查询快递信息、快递公司注册成为合作伙伴以及系统管理员对信息进行管理等功能。系统的主要界面会将所有的服务排列好&…

Typora段内公式

文件——>偏好设置——>Markdown——>内联公式 $$ 输入公式 $$

【grpc】二、grpc入门,基本使用方法

上篇介绍了使用protobuf生成grpc相关代码&#xff0c;并实现了服务端方法&#xff0c;本篇介绍下具体的使用。 一、服务端 // server.gopackage mainimport ("google.golang.org/grpc""grpcDemo/calc""net" )func startServer(addr string) {//…

【C语言】函数相关选择题

前言 关于函数相关的选择题。 题目一&#xff1a; C语言规定&#xff0c;在一个源程序中&#xff0c;main函数的位置&#xff08; &#xff09; A .必须在最开始 B .必须在库函数的后面 C .可以任意 D .必须在最后 题解&#xff1a;选择C。 main函数为C语言中整个工程的程序入…

快速理解vim编辑器和软硬连接

刚开始学习linux基础命令时最难搞懂的应该就是vi和ln命令了&#x1f62d;&#xff0c;写这篇文章主要想和大家分享一下我对这两个命令的理解&#xff0c;希望能更快的帮助到那些还没搞懂的同学&#x1f61c; 首先我们要知道vim编辑器主要分为三个工作模式&#xff0c;分别是图上…

python中的split()用法

在Python中&#xff0c;split() 是一个字符串方法&#xff0c;用于将字符串按照指定的分隔符分割成一个列表。如果没有提供分隔符&#xff0c;那么它会默认按照任何空白字符&#xff08;如空格、换行符、制表符等&#xff09;进行分割。 这里是 split() 方法的一些基本用法&am…

K8s学习九(配置与存储_存储)

存储管理 Volumes HostPath 将节点上的文件或目录挂载到 Pod 上&#xff0c;此时该目录会变成持久化存储目录&#xff0c;即使 Pod 被删除后重启&#xff0c;也可以重新加载到该目录&#xff0c;该目录下的文件不会丢失 效果就是容器里的数据和主机里的数据进行共享 配置文…

Hadoop安装部署-SecondaryNameNode备份版

Hadoop分布式文件系统支持NameNode节点的高可用性&#xff0c;本文主要描述Secondary NameNode数据备份版本的安装部署。 如上所示&#xff0c;NameNode主节点同步数据到NameNode备份节点&#xff0c;当NameNode主节点发生故障变得不可用时&#xff0c; NameNode主节点重新启动…