设计模式第19讲——命令模式(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),它定义了点菜和取消点菜的方法

/**
 * @author Created by njy on 2023/6/29
 * 1.抽象命令(Command): 点菜和取消两个命令
 * 定义:命令是一个抽象接口,定义了执行操作的统一方法。
 */
public interface Command {
    //点菜
    void order();
    //取消点菜
    void cancelOrder();
}

5.2 接收者(Receiver)——Chef

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

/**
 * @author Created by njy on 2023/6/29
 * 2.接收者(Receiver):厨师
 * 定义:执行实际命令的类,命令对象会调用接收者的方法来执行请求。
 */
public class Chef {

    public void cook() {
        System.out.println("厨师执行点菜命令:正在烹饪菜品...");
    }

    public void cancelCooking() {
        System.out.println("厨师执行取消命令:停止烹饪菜品!");
    }
}

5.3 具体命令(Concrete Command)——OrderCommand

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

/**
 * @author Created by njy on 2023/6/29
 * 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();
    }
}

5.4 调用者(Invoker)——Waiter

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

/**
 * @author Created by njy on 2023/6/29
 * 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();
    }
}

5.5 testCommand

/**
 * @author Created by njy on 2023/6/29
 * 命令模式测试类
 */
@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();
    }
}

六、总结

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

END:更多设计模式的介绍,推荐移步至👉 23种设计模式学习导航(Java完整版)👈 

  • 24
    点赞
  • 122
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使得请求的发送者和接收者解耦。在命令模式中,请求以命令的形式包裹在对象中,并传递给调用对象。调用对象寻找可以处理该命令的合适的对象,并将命令传递给相应的对象,该对象执行命令。 在C语言中,可以使用函数指针来实现命令模式。具体步骤如下: 1. 定义一个命令接口,该接口包含一个执行命令的方法。 2. 创建具体的命令类,实现命令接口,并在执行方法中调用相应的函数。 3. 创建一个调用者类,该类包含一个命令对象,并提供一个执行命令的方法。 4. 在调用者类中,将命令对象传递给相应的对象,并调用命令对象的执行方法。 下面是一个简单的示例代码: ```c #include <stdio.h> // 定义命令接口 typedef struct { void (*execute)(void); } Command; // 创建具体的命令类 typedef struct { Command command; void (*function)(void); } ConcreteCommand; void concreteCommand_execute(void) { printf("执行具体的命令\n"); } // 创建调用者类 typedef struct { Command *command; void (*setCommand)(Command *command); void (*executeCommand)(void); } Invoker; void invoker_setCommand(Command *command) { Invoker *invoker = (Invoker *)command; invoker->command = command;} void invoker_executeCommand(void) { Invoker *invoker = (Invoker *)invoker->command; invoker->command->execute(); } int main() { // 创建具体的命令对象 ConcreteCommand concreteCommand; concreteCommand.command.execute = concreteCommand_execute; concreteCommand.function = concreteCommand_execute; // 创建调用者对象 Invoker invoker; invoker.setCommand((Command *)&concreteCommand); invoker.executeCommand(); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橡 皮 人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值