• 14、设计模式之命令模式(Command)


    一、什么是命令模式
    命令模式(Command Pattern)是一种行为型设计模式,又叫动作模式或事务模式。它将请求(命令)封装成对象,使得可以用不同的请求对客户端进行参数化,具体的请求可以在运行时更改、排队或记录,它讲发出者和接收者解耦(顺序:发出者–>命令–>接收者)
    本质:封装请求

    二、角色组成

    抽象命令(Command):命令是一个抽象接口,定义了执行操作的统一方法。具体的命令类会实现这个接口,并提供执行相应操作的具体逻辑。
    具体命令(Concrete Command):具体命令类实现了抽象命令,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
    接收者(Receiver):执行实际命令的类,命令对象会调用接收者的方法来执行请求。
    调用者(Invoker):持有命令对象,通常是多个,并通过访问命令对象来执行相关请求,他不直接访问接收者。

    三、优缺点
    优点:

    解耦:命令模式可以将发送命令的对象和执行命令的对象解耦,使得两者可以独立变化。
    可扩展性:可以方便地添加新的命令类和接收者类,而无需修改现有的代码结构。
    容易实现撤销和重做功能:由于每个命令对象都封装了具体的操作,可以很容易地实现命令的撤销和重做功能。
    支持操作的队列化和延迟执行:命令对象可以被组织成队列或堆栈,实现对操作的排队和延迟执行。
    缺点:

    增加了类和对象的数量:使用命令模式可能会增加一些新的类和对象,从而增加了代码的复杂性。
    需要额外的开销:封装命令对象和操作会增加一些额外的开销,可能会稍微降低性能。
    可能导致过多的具体命令类:如果系统的命令比较多,可能会导致需要创建很多具体的命令类,增加了代码维护的难度。
    四、应用场景
    4.1 生活场景
    餐厅点餐:在一家餐厅中,服务员充当调用者,厨师充当接收者,菜品可以作为具体命令。当顾客想点菜时,服务员会将顾客的需求封装成一个命令对象,并传递给厨师。厨师根据命令对象中的信息来完成相应的烹饪工作。这样,顾客和厨师之间不需要直接沟通,而是通过命令对象来实现点餐和烹饪的解耦。
    遥控器控制家电:拿电视遥控器举例,遥控器是调用者,电视是接收者。每个按键都可以看作是一个具体命令,例如音量加、音量减、切换频道等。当用户按下某个按键时,遥控器会发送相应的命令给电视,然后电视根据命令执行相应的操作,如增加音量、减小音量或切换频道。
    4.2 java场景

    Runnable接口:Java中的Runnable接口就是一个典型的命令模式的应用。Runnable接口封装了需要执行的任务,然后可以交给线程去执行。
    Timer和TimerTask类:这两个类用于定时任务调度,TimerTask类封装了要执行的任务,然后由Timer类作为调用者执行这些任务。
    Statement接口:在Java中与数据库交互时,SQL语句被封装成Statement对象,然后由数据库驱动程序执行相应的命令。

    五、代码实现
    下面以餐厅点餐为例,解释以下命令模式。在一家餐厅中,服务员充当调用者,厨师充当接收者,菜品可以作为具体命令。当顾客想点菜时,服务员会将顾客的需求封装成一个命令对象,并传递给厨师。厨师根据命令对象中的信息来完成相应的烹饪工作。

    抽象命令:Command
    具体命令:OrderCommand
    接收者:Chef
    调用者:Waiter

    5.0 UML类图
    在这里插入图片描述
    5.1 抽象命令(Command)——Command
    首先,创建一个命令接口(Command),它定义了点菜和取消点菜的方法

    /**
     *
     * 1.抽象命令(Command): 点菜和取消两个命令
     * 定义:命令是一个抽象接口,定义了执行操作的统一方法。
     */
    public interface Command {
        //点菜
        void order();
        //取消点菜
        void cancelOrder();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.2 接收者(Receiver)——Chef
    创建接收者类(Chef)实现具体的烹饪操作。

    /**
     * 
     * 2.接收者(Receiver):厨师
     * 定义:执行实际命令的类,命令对象会调用接收者的方法来执行请求。
     */
    public class Chef {
     
        public void cook() {
            System.out.println("厨师执行点菜命令:正在烹饪菜品...");
        }
     
        public void cancelCooking() {
            System.out.println("厨师执行取消命令:停止烹饪菜品!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.3 具体命令(Concrete Command)——OrderCommand
    创建具体命令类(如点菜命令)实现命令接口,并将点菜的请求和具体的烹饪者(厨师)关联起来

    /**
     * 
     * 3.具体命令(Concrete Command):点菜命令
     * 定义:具体命令类实现了抽象命令,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
     */
    public class OrderCommand implements Command{
        // 厨师
        private Chef chef;
     
        public OrderCommand(Chef chef) {
            this.chef = chef;
        }
     
        public void order() {
            //与具体的烹饪者(厨师)关联,执行点菜操作
            chef.cook();
        }
     
        public void cancelOrder() {
            //与具体的烹饪者(厨师)关联,执行取消点菜操作
            chef.cancelCooking();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    5.4 调用者(Invoker)——Waiter

    最后,在服务员类中,创建调用者(服务员),接收命令(点菜命令),并执行相应的操作

    /**
     *
     * 4.调用者(Invoker):服务员
     * 定义:持有命令对象,通常是多个,并通过访问命令对象来执行相关请求,他不直接访问接收者。
     */
    public class Waiter {
        //命令对象
        private Command command;
     
        public void setCommand(Command command) {
            this.command = command;
        }
     
        public void takeOrder() {
            // 服务员接收到顾客的点菜请求
            System.out.println("服务员接收到顾客(客户端)点菜请求!");
            // 执行点菜操作
            command.order();
        }
     
        public void cancelOrder() {
            // 服务员收到顾客的取消点菜请求
            System.out.println("服务员接收到顾客(客户端)取消点菜请求!");
            // 执行取消点菜操作
            command.cancelOrder();
        }
    }
    
    • 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

    5.5 testCommand

    /**
     * 
     * 命令模式测试类
     */
    @SpringBootTest
    public class TestCommand {
     
        @Test
        void testCommand(){
            // 创建厨师(接收者)
            Chef chef = new Chef();
            // 创建点菜命令
            Command orderCommand = new OrderCommand(chef);
            // 创建服务员(调用者)
            Waiter waiter = new Waiter();
            // 设置命令
            waiter.setCommand(orderCommand);
            // 服务员接收到点菜请求
            waiter.takeOrder();
            // 服务员接收到取消点菜请求
            waiter.cancelOrder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述
    六、总结
    命令模式适用于需要将请求封装成对象,实现请求发出者和接收者之间的解耦,并支持撤销、队列化和延迟执行的场景。它虽然可以提高系统的可扩展性和灵活性,但是需要权衡额外的开销和复杂性。

  • 相关阅读:
    git clone:SSL: no alternative certificate subject name matches target host name
    GBase 8c V3.0.0数据类型——几何操作符
    MemStream: Memory-Based Streaming Anomaly Detection
    收钱吧研发效能实践之工具篇
    Git 客户端基本使用及新手常见问题
    华为1000人校园实验记录
    55.【sort函数的升序降序】
    iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)
    ZYNQ之FPGA学习----UART串口实验
    JavaScript宏任务和微任务(setTimeout,Promise)
  • 原文地址:https://blog.csdn.net/weixin_45817985/article/details/136670228