• 命令模式


    思考命令模式

    命令模式就是通过将请求封装成对象,实现请求的发送者和接收者之间的解耦,同时支持队列操作、日志记录和撤销操作。

    1.命令模式的本质

    命令模式的本质:封装请求。

    命令模式的本质是将请求封装成一个对象,使得可以用不同的请求对客户端进行参数化,支持队列操作、日志记录和撤销操作,并且能够实现请求的发送者和接收者之间的解耦。

    什么是将请求封装成一个对象,使得可以用不同的请求对客户端进行参数化?

    例如下面的厨师做菜案例, 我们可以定义一个抽象的命令接口 Command,其中包含一个执行方法 execute()。然后,我们可以创建具体的命令类,如 FishCommand(做鱼香肉丝的命令) 和 MeatCommand(做北京烤鸭的命令),每个类封装了不同的请求操作。

    客户端可以根据需要创建不同的命令对象,并将这些命令对象传递给请求者对象(如菜单)。这样,不同的请求操作可以通过命令对象的参数化来实现,而不需要直接调用具体的请求操作。

    2.何时选用命令模式

    建议在以下情况时选用命令模式。

    • 当需要将请求操作的调用者和接收者解耦时,以便能够灵活地对它们进行变化和扩展。
    • 当需要对请求进行参数化,使得可以在不同的请求之间进行切换和组合,从而实现动态的请求操作。
    • 当需要支持命令的撤销、恢复、排队、日志记录等特性时。
    • 当需要实现任务调度、异步处理、队列操作等场景时。
    • 当需要构建具有嵌套命令结构的系统,以支持复杂的操作和组合。

    典型场景:

    • 菜单和按钮操作:在图形用户界面中,可以将不同的菜单项和按钮操作封装成具体的命令对象,并将其与相应的请求操作关联。这样,可以轻松地添加、修改和组合不同的命令,实现灵活的界面交互。

    • 任务调度和异步处理:命令模式可以用于实现任务调度器,将任务封装成命令对象并按照需要进行调度和执行。同时,可以支持异步处理和任务队列,以提高系统的并发性和性能。

    • 撤销和恢复操作:命令模式可以记录命令的执行历史,使得可以轻松地实现撤销和恢复操作。每个命令对象可以保存执行所需的状态和参数,从而可以在需要时撤销执行操作,或者重新执行先前的操作。

    • 日志记录和审计功能:由于每个命令对象都封装了执行操作的细节和参数,可以很容易地记录和存储命令对象。这样可以实现日志记录和审计功能,用于跟踪和监控系统的操作。

    • 复杂操作和组合:命令模式支持构建具有嵌套命令结构的系统,以支持复杂的操作和组合。可以将多个命令对象组合成一个复合命令,从而实现更高层次的操作和控制。

    3.优缺点

    命令模式的优点

    • 解耦调用者和接收者
      命令模式将请求操作封装成命令对象,使得调用者和接收者之间解耦,调用者无需知道具体的接收者,只需调用命令对象即可,从而提高了系统的灵活性和可维护性。

    • 容易扩展新的命令
      由于命令模式将请求操作封装成命令对象,因此非常容易添加新的命令类,而无需修改现有的代码,符合开闭原则

    • 支持队列操作和撤销操
      命令模式可以将命令对象放入队列中,实现命令的排队、延迟和调度等操作。同时,由于命令对象封装了操作的状态和参数,可以实现命令的撤销和恢复。

    • 支持日志记录和审计
      由于命令对象封装了请求操作的细节和参数,可以很容易地记录和存储命令对象,实现日志记录和审计功能。

    缺点。

    • 类膨胀
      使用命令模式会增加代码量,因为每个具体的命令都需要一个单独的类来实现。如果命令非常多,可能会导致类的数量剧增,增加系统的复杂性。
    • 可能引入过多的细粒度命令对象
      在一些简单的场景下,命令模式可能会引入过多的细粒度命令对象,增加了系统的开销。

    4.命令模式的结构

    在这里插入图片描述

    • Command:定义命令的接口,声明执行的方法。
    • ConcreteCommand:命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
    • Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
    • Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
    • Client:创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。

    5.实现

    耦合写法

    模拟电脑开机,点击机箱开机按钮,调用主板初始化系统,然后用户就能操作了

    1.主板类

    /**
     * @description:主板接口
     */
    public interface MainBoardApi {
    
        /**
         * 开机
         */
        void open();
    }
    
    /**
     * @description:技嘉主板
     */
    public class JiJiaMainBoard implements MainBoardApi{
    
        @Override
        public void open() {
            System.out.println("技嘉主板正在开机,请稍后");
            System.out.println("接通电源.............");
            System.out.println("设备检查.............");
            System.out.println("装载系统.............");
            System.out.println("机器正常运行,请操作....");
        }
    }
    
    /**
     * @description:微星主板
     */
    public class WeiXinMainBoard implements MainBoardApi{
    
        @Override
        public void open() {
            System.out.println("微星主板正在开机,请稍后");
            System.out.println("接通电源.............");
            System.out.println("设备检查.............");
            System.out.println("装载系统.............");
            System.out.println("机器正常运行,请操作....");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    2.开机按钮类

    /**
     * @description:机箱开机按钮
     */
    public class BoxButton {
    
        /**
         * 点击开机按钮,就开机
         */
        public void boot(int flag){
            if (1==flag){
                new JiJiaMainBoard().open();
            }else {
                new WeiXinMainBoard().open();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.测试类

    /**
     * @description:最开始耦合写法
     */
    public class Test1 {
    
        public static void main(String[] args) {
            //点击按钮开机
            new BoxButton().boot(2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    现在,机箱按钮直接调用主板,是强耦合的关系,很不利于维护
    在这里插入图片描述

    命令模式优化耦合写法

    改造上面的耦合写法,主板接口和实现类不变

    1.调用程序类(也就是上面的机箱按钮)

    /**
     * @description:调用程序(机箱开机按钮)
     */
    public class Invoker {
    
        /**
         * 持有命令对象
         */
        private Command command=null;
    
        public void setCommand(Command command) {
            this.command = command;
        }
    
        /**
         * 开机
         */
        public void boot(){
            command.execute();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.命令类

    /**
     * @description:命令接口
     */
    public interface Command {
    
        /**
         * 执行命令
         */
        void execute();
    }
    
    /**
     * @description:具体命令类(这里是开机命令)
     */
    @AllArgsConstructor
    public class ConcreteCommand implements Command{
    
        /**
         * 持有主板对象
         */
        private MainBoardApi mainBoardApi;
    
        @Override
        public void execute() {
            //命令类不能进行开机操作
            //调用主板进行开机
            mainBoardApi.open();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3.测试类

    /**
     * @description:测试类
     * @createTime 2022/11/30 13:06
     */
    public class Client {
    
        public static void main(String[] args) {
            //把命令和实现组装起来
            Command command=new ConcreteCommand(new WeiXinMainBoard());
            //为机箱按钮设置命令
            Invoker invoker = new Invoker();
            invoker.setCommand(command);
            //模拟开机按钮
            invoker.boot();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.效果
    在这里插入图片描述

    在这里插入图片描述

    命令模式实现撤销

    撤销有两种:

    • 补偿式(反操作式)
    • 存储恢复式

    模拟假如存在一个存在一个撤销按钮,电脑开机后,点击撤销按钮,撤销开机操作,也就是进行关机

    1.主板类增加关机功能

    /**
     * @description:主板接口
     */
    public interface MainBoardApi {
    
        /**
         * 开机
         */
        void open();
    
        /**
         * 关机
         */
        void close();
    }
    
    /**
     * @description:技嘉主板
     */
    public class JiJiaMainBoard implements MainBoardApi{
    
        @Override
        public void open() {
            System.out.println("技嘉主板正在开机,请稍后");
            System.out.println("接通电源.............");
            System.out.println("设备检查.............");
            System.out.println("装载系统.............");
            System.out.println("机器正常运行,请操作....");
        }
    
        @Override
        public void close() {
            System.out.println("技嘉主板正在关机,请稍后");
            System.out.println("关机成功.............");
        }
    }
    
    /**
     * @description:微星主板
     */
    public class WeiXinMainBoard implements MainBoardApi{
    
        @Override
        public void open() {
            System.out.println("微星主板正在开机,请稍后");
            System.out.println("接通电源.............");
            System.out.println("设备检查.............");
            System.out.println("装载系统.............");
            System.out.println("机器正常运行,请操作....");
        }
    
        @Override
        public void close() {
            System.out.println("微星主板正在关机,请稍后");
            System.out.println("关机成功.............");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    2.命令接口增加撤销命令

    /**
     * @description:命令接口
     */
    public interface Command {
    
        /**
         * 执行命令
         */
        void execute();
    
        /**
         * 撤销命令
         */
        void undo();
    }
    
    /**
     * @description:具体命令类(这里是开机命令)
     */
    @AllArgsConstructor
    public class ConcreteCommand implements Command{
    
        /**
         * 持有主板对象
         */
        private MainBoardApi mainBoardApi;
    
        @Override
        public void execute() {
            //命令类不能进行开机操作
            //调用主板进行开机
            mainBoardApi.open();
        }
    
        @Override
        public void undo() {
            //撤销开机,也就是关机
            mainBoardApi.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3.调用程序增加关机功能

    /**
     * @description:调用程序(机箱开机按钮)
     */
    public class Invoker {
    
        /**
         * 持有命令对象
         */
        private Command command=null;
    
        public void setCommand(Command command) {
            this.command = command;
        }
    
        /**
         * 开机
         */
        public void boot(){
            command.execute();
        }
    
        /**
         * 关机
         */
        public void shutdown(){
            command.undo();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4.测试类

    public class Client {
    
        public static void main(String[] args) {
            //把命令和实现组装起来
            Command command=new ConcreteCommand(new WeiXinMainBoard());
            //为机箱按钮设置命令
            Invoker invoker = new Invoker();
            invoker.setCommand(command);
            //模拟开机按钮
            invoker.boot();
    
            //模拟点击撤销按钮,电脑关机
            invoker.shutdown();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.结果
    在这里插入图片描述

    命令模式实现厨师做菜

    模拟两个厨师,一个做热菜,一个做凉菜,服务员点菜
    在这里插入图片描述

    1.厨师类

    /**
     * @description:厨师接口
     */
    public interface CookApi {
    
        /**
         * 做菜
         * @param name
         */
        void cook(String name);
    }
    
    /**
     * @description:热菜
     */
    public class HotCook implements CookApi{
        @Override
        public void cook(String name) {
            System.out.println("厨师正在做:"+name);
        }
    }
    
    /**
     * @description:凉菜
     */
    public class CoolCook implements CookApi{
        @Override
        public void cook(String name) {
            System.out.println("厨师正在做:"+name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.命令类

    /**
     * @description:命令接口
     */
    public interface Command {
    
        /**
         * 执行命令
         */
        void execute();
    }
    
    /**
     * @description:生鱼刺身
     */
    public class FishCommand implements Command{
    
        private CookApi cookApi=null;
    
        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }
    
        @Override
        public void execute() {
            cookApi.cook("生鱼刺身");
        }
    }
    
    /**
     * @description:北京烤鸭
     */
    public class MeatCommand implements Command{
    
        private CookApi cookApi=null;
    
        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }
    
        @Override
        public void execute() {
            cookApi.cook("北京烤鸭");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    3.菜单类

    /**
     * @description:菜单
     */
    public class Menu{
    
        private Collection<Command> menu=new ArrayList<>();
    
        /**
         * 点菜,将菜品加入菜单
         * @param cmd
         */
        public void addCommand(Command cmd){
            menu.add(cmd);
        }
    
        public void execute() {
            //遍历菜单,做菜
            for (Command cmd:menu){
                cmd.execute();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.服务员(调用程序)

    /**
     * @description:调用程序(服务员)
     */
    public class Waiter {
    
        private Menu menu=new Menu();
    
        /**
         * 客人点菜
         * @param cmd
         */
        public void orderDish(Command cmd){
            //判断菜品是热菜还是凉菜
            if (cmd instanceof FishCommand){
                ((FishCommand)cmd).setCookApi(new HotCook());
            }else {
                ((MeatCommand)cmd).setCookApi(new CoolCook());
            }
            //加到菜单中
            menu.addCommand(cmd);
        }
    
        /**
         * 点菜完毕,执行命令去做菜
         */
        public void orderOver(){
            menu.execute();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5.测试类

    public class Client {
    
        public static void main(String[] args) {
            Waiter waiter = new Waiter();
    
            //点菜
            waiter.orderDish(new FishCommand());
            waiter.orderDish(new MeatCommand());
    
            //点菜完毕
            waiter.orderOver();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6.结果
    在这里插入图片描述

    命令模式实现排队

    在这里插入图片描述
    修改代码如下

    1.测试类

    public class Client {
    
        public static void main(String[] args) {
    
            //创建3位厨师
            HotCook cook1 = new HotCook("张三");
            HotCook cook2 = new HotCook("李四");
            HotCook cook3 = new HotCook("王五");
    
            //启动线程
            new Thread(cook1).start();
            new Thread(cook2).start();
            new Thread(cook3).start();
    
            //模拟10桌客人
            for (int i=0;i<10;i++){
                Waiter waiter = new Waiter();
                //每个客人都点了北京烤鸭和生鱼刺身
                waiter.orderDish(new FishCommand(i));
                waiter.orderDish(new MeatCommand(i));
                //点菜完毕
                waiter.orderOver();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.服务员类

    /**
     * @description:调用程序(服务员)
     */
    public class Waiter {
    
        private Menu menu =new Menu();
    
        /**
         * 客人点菜
         * @param cmd
         */
        public void orderDish(Command cmd){
            //加到菜单中
            menu.addCommand(cmd);
        }
    
        /**
         * 点菜完毕,执行命令去做菜
         */
        public void orderOver(){
            menu.execute();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.菜单类

    /**
     * @description:菜单
     */
    public class Menu {
    
        private Collection<Command> menu=new ArrayList<>();
    
        /**
         * 点菜,将菜品加入菜单
         * @param cmd
         */
        public void addCommand(Command cmd){
            menu.add(cmd);
        }
    
        /**
         * 获取菜单中所有命令
         * @return
         */
        public Collection<Command> getCommands(){
            return this.menu;
        }
    
        public void execute() {
            //将菜单传给后厨
            CommandQueue.addMenu(this);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4.后厨管理类(队列)

    /**
     * @description:菜单队列
     */
    public class CommandQueue {
    
        private static List<Command> cmds=new ArrayList<>();
    
        /**
         * 将菜单中做菜的命令取出来,加入一个队列
         * @param menu
         */
        public synchronized static void addMenu(Menu menu){
            for (Command cmd:menu.getCommands()){
                cmds.add(cmd);
            }
        }
    
        /**
         * 按顺序取出一个命令
         * @return
         */
        public synchronized static Command getOneCommand(){
            Command cmd=null;
            if (cmds.size()>0){
                //按顺序取出第一个命令
                cmd=cmds.get(0);
                //取完就移除
                cmds.remove(0);
            }
            return cmd;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    5.命令类

    /**
     * @description:命令接口
     */
    public interface Command {
    
        /**
         * 执行命令
         */
        void execute();
    
        /**
         * 设置命令接收者
         * @param cookApi
         */
        void setCookApi(CookApi cookApi);
    }
    
    /**
     * @description:生鱼刺身
     */
    public class FishCommand implements Command{
    
        private CookApi cookApi=null;
    
        /**
         * 桌号
         */
        private int tableId;
    
        @Override
        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }
    
        public FishCommand(int tableId) {
            this.tableId = tableId;
        }
    
        @Override
        public void execute() {
            cookApi.cook(tableId,"生鱼刺身");
        }
    }
    
    /**
     * @description:北京烤鸭
     */
    public class MeatCommand implements Command{
    
        private CookApi cookApi=null;
    
        /**
         * 桌号
         */
        private int tableId;
    
        @Override
        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }
    
        public MeatCommand(int tableId) {
            this.tableId = tableId;
        }
    
        @Override
        public void execute() {
            cookApi.cook(tableId,"北京烤鸭");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    6.厨师类,暂时只用了一个实现类,可以不使用接口

    /**
     * @description:厨师接口
     */
    public interface CookApi {
    
        /**
         * 做菜
         * @param tableId 桌号
         * @param name
         */
        void cook(int tableId,String name);
    }
    
    /**
     * @description:热菜
     */
    @AllArgsConstructor
    public class HotCook implements CookApi,Runnable{
        private String name;
    
        @Override
        public void cook(int tableId, String name) {
            //做菜时间
            int cookTime=(int)(20*Math.random());
            System.out.println(this.name+"厨师正在做"+tableId+"号桌的热菜:"+name);
    
            try {
                Thread.sleep(cookTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name+"厨师做好了"+tableId+"号桌的热菜:"+name+",共耗时"+cookTime);
        }
    
        @Override
        public void run() {
            while (true){
                //从后厨取一个做菜命令
                Command oneCommand = CommandQueue.getOneCommand();
                if (oneCommand!=null){
                    //设置自己为做菜厨师
                    oneCommand.setCookApi(this);
                    //执行命令
                    oneCommand.execute();
                }
                //做完菜休息1s
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    命令模式实现日志持久化

    在这里插入图片描述

    1.FishCommand和MeatCommand实现序列化接口

    public class MeatCommand implements Command, Serializable {}
    
    public class FishCommand implements Command, Serializable {}
    
    • 1
    • 2
    • 3

    2.序列化和反序列化工具类

    /**
     * @description:文件操作类
     */
    public class FileUtil {
    
        /**
         * 读文件,反序列化
         * @param pathName
         * @return
         */
        public static List readFile(String pathName){
            List list=new ArrayList();
            ObjectInputStream oin=null;
            File file = new File(pathName);
            if (file.exists()){
                try {
                    oin=new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
                    //反序列化
                    list= (List) oin.readObject();
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    if (oin!=null){
                        try {
                            oin.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return list;
        }
    
        /**
         * 写入文件,序列化
         * @param pathName
         * @param list
         */
        public static void writeFile(String pathName,List list){
            File file = new File(pathName);
            ObjectOutputStream oos=null;
            try {
                oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
                //序列化
                oos.writeObject(list);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if (oos!=null){
                    try {
                        oos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    3.后厨管理类

    /**
     * @description:菜单队列
     */
    public class CommandQueue {
    
        private final static String FILE_NAME="C:\\Users\\Lenovo\\Desktop\\commandqueue.txt";
    
        private static List<Command> cmds=null;
    
        /**
         * 静态代码块,每次重启先加载
         */
        static {
            cmds= FileUtil.readFile(FILE_NAME);
            if (cmds==null){
                cmds=new ArrayList<>();
            }
        }
    
        /**
         * 将菜单中做菜的命令取出来,加入一个队列
         * @param menu
         */
        public synchronized static void addMenu(Menu menu){
            for (Command cmd:menu.getCommands()){
                cmds.add(cmd);
            }
            //覆盖写入文件
            FileUtil.writeFile(FILE_NAME,cmds);
        }
    
        /**
         * 按顺序取出一个命令
         * @return
         */
        public synchronized static Command getOneCommand(){
            Command cmd=null;
            if (cmds.size()>0){
                //按顺序取出第一个命令
                cmd=cmds.get(0);
                //取完就移除
                cmds.remove(0);
                //覆盖写入文件
                FileUtil.writeFile(FILE_NAME,cmds);
            }
            return cmd;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    执行
    在这里插入图片描述

    在第八桌的时候终止程序,然后再重启看效果,会从上次中断的地方继续做菜,然后再开始新得做菜
    在这里插入图片描述

  • 相关阅读:
    Git分支合并别的分支代码
    药智网数据库介绍
    es6中proxy如何使用
    【从0到1开发一个网关】网关日志的开发
    GitHub霸榜月余的24万字Java面试手册,竟是如此牛逼
    PostgreSQL下载和安装教程
    深度解析:Stable Diffusion中negative prompt是如何作用的?
    Spring 源码(7)Spring的注解是如何解析的?
    【软件分析/静态分析】学习笔记01——Introduction
    人工智能基础 作业6
  • 原文地址:https://blog.csdn.net/qq_42665745/article/details/128111377