【设计模式系列笔记】命令模式

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 Tair(兼容Redis),内存型 2GB
简介: 命令模式是一种行为设计模式,它将请求封装成一个对象,从而允许使用不同的请求、队列或日志请求参数化客户端对象,同时支持撤销操作。

1. 命令模式介绍

命令模式是一种行为设计模式,它将请求封装成一个对象,从而允许使用不同的请求、队列或日志请求参数化客户端对象,同时支持撤销操作。在Java中,命令模式通常涉及以下几个角色:

  1. 命令接口(Command):定义执行操作的接口。
  2. 具体命令类(ConcreteCommand):实现命令接口,封装了执行操作的具体逻辑。
  3. 调用者或请求者(Invoker):负责调用命令对象执行请求。
  4. 接收者(Receiver):知道如何实施与执行一个请求相关的操作。
  5. 客户端(Client):创建具体命令对象并设置其接收者。

2. 关键思想

  1. 封装命令: 命令模式的核心是将一个请求封装为一个对象,这个对象包含了执行请求所需的所有信息,包括命令的接收者、请求的方法调用以及参数等。
  2. 解耦调用者和接收者: 命令模式通过封装命令对象,使得调用者和接收者之间解耦。调用者不需要知道接收者的具体实现,只需通过命令对象来间接调用接收者的方法。
  3. 支持撤销操作: 命令对象通常包含一个撤销(undo)操作,允许系统能够回滚先前的命令。这通过在命令对象中添加一个逆操作来实现。
  4. 支持队列和日志: 由于命令被封装为对象,可以方便地将命令对象放入队列中,实现对命令的排队和执行。同时,也可以将命令对象记录到日志中,实现对系统操作的记录和重放。
  5. 提高系统灵活性和可扩展性: 命令模式使得系统更容易扩展,可以通过添加新的命令类来实现新的功能,而无需修改现有的代码。
  6. 降低调用者的复杂性: 调用者不需要了解命令的具体实现,只需要知道如何使用命令对象即可。这降低了调用者的复杂性,使得系统更容易维护和理解。

总的来说,命令模式的关键思想在于将请求封装为对象,从而提供了一种松耦合的设计,支持撤销操作和队列执行,同时提高了系统的灵活性和可扩展性。

3. 实现方式

命令模式的实现方法通常涉及以下几个关键步骤:

  1. 命令接口定义: 定义一个命令接口,其中包含执行命令的方法,例如execute()
// 命令接口
public interface Command {
    void execute();
}
  1. 具体命令类实现: 实现命令接口的具体命令类,负责执行具体的操作。
// 具体命令类
public class ConcreteCommand implements Command {
    private Receiver receiver;
    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.action();
    }
}
  1. 接收者定义: 定义接收者类,其中包含具体的操作方法。
javaCopy code
// 接收者
public class Receiver {
    public void action() {
        System.out.println("执行操作");
    }
}
  1. 调用者实现: 实现调用者类,负责设置命令对象并触发执行。
// 调用者
public class Invoker {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void executeCommand() {
        command.execute();
    }
}
  1. 客户端使用: 在客户端中创建具体的命令对象和接收者对象,并设置它们之间的关系,然后通过调用者来执行命令。
// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建接收者
        Receiver receiver = new Receiver();
        // 创建具体命令并设置接收者
        Command command = new ConcreteCommand(receiver);
        // 创建调用者并设置命令
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        // 调用者执行命令
        invoker.executeCommand();
    }
}

这个例子中,通过将请求封装成Command对象,实现了调用者(Invoker)和接收者(Receiver)之间的解耦。客户端可以轻松地创建不同的命令对象,并通过调用者执行它们。这种实现方式提高了系统的灵活性和可扩展性,同时也支持撤销、重做等操作。

示例代码

考虑一个餐厅订单系统的例子,使用命令模式来处理顾客的点餐请求。在这个场景中,服务员充当调用者,顾客的点餐请求则是命令,而厨师是接收者。

// 命令接口
public interface Order {
    void execute();
}
// 具体命令类 - 点餐命令
public class PlaceOrderCommand implements Order {
    private Chef chef; // 命令对象持有对应的接收者,这里是厨师
    // 构造方法,传入对应的厨师对象
    public PlaceOrderCommand(Chef chef) {
        this.chef = chef;
    }
    // 实现命令接口的方法,调用接收者(厨师)的烹饪方法
    @Override
    public void execute() {
        chef.cook(); // 调用厨师的烹饪方法
    }
}
// 接收者 - 厨师
public class Chef {
    public void cook() {
        System.out.println("厨师开始烹饪");
    }
}
// 调用者 - 服务员
public class Waiter {
    private Order order; // 服务员持有一个命令对象,用于执行顾客的点餐请求
    // 设置命令对象的方法,用于客户端设置服务员的命令
    public void setOrder(Order order) {
        this.order = order;
    }
    // 执行命令的方法,表示服务员接收顾客的点餐请求并执行
    public void takeOrder() {
        order.execute(); // 调用命令对象的execute方法,实际上执行了具体的点餐操作
    }
}
// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建接收者对象
        Chef chef = new Chef();
        // 创建具体命令对象并设置接收者
        Order placeOrderCommand = new PlaceOrderCommand(chef);
        // 创建调用者对象并设置命令
        Waiter waiter = new Waiter();
        waiter.setOrder(placeOrderCommand);
        // 调用者执行命令
        waiter.takeOrder(); // 输出:"厨师开始烹饪"
    }
}

在这个例子中,顾客通过服务员点餐,服务员负责将顾客的点餐请求封装成PlaceOrderCommand命令对象,并交给厨师(Chef)来执行烹饪操作。通过这种方式,我们实现了点餐请求的解耦,服务员和厨师之间不直接耦合,从而使得系统更加灵活和可扩展。添加新的菜品只需创建新的具体命令对象即可,而不需要修改现有的代码。

要点:

  1. 封装请求: 将一个请求封装成一个对象,包括请求的参数、方法调用等信息。这个对象就是命令对象,实现了一个命令接口。
  2. 解耦调用者和接收者: 命令模式通过封装命令对象,使得调用者和接收者之间解耦。调用者不需要知道接收者的具体实现,只需通过命令对象来间接调用接收者的方法。
  3. 支持撤销操作: 命令对象通常包含一个撤销(undo)操作,允许系统能够回滚先前的命令。这通过在命令对象中添加一个逆操作来实现。
  4. 支持队列和日志: 由于命令被封装为对象,可以方便地将命令对象放入队列中,实现对命令的排队和执行。同时,也可以将命令对象记录到日志中,实现对系统操作的记录和重放。
  5. 提高系统灵活性和可扩展性: 命令模式使得系统更容易扩展,可以通过添加新的命令类来实现新的功能,而无需修改现有的代码。
  6. 降低调用者的复杂性: 调用者不需要了解命令的具体实现,只需要知道如何使用命令对象即可。这降低了调用者的复杂性,使得系统更容易维护和理解。

注意事项:

  1. 适用场景: 命令模式适用于需要将请求发送者和接收者解耦,支持撤销和重做,支持命令排队和队列,以及支持日志记录等场景。
  2. 撤销操作的实现: 如果要支持撤销操作,需要在命令接口中添加撤销操作的方法,并在具体命令类中实现。
  3. 调用者和接收者的解耦: 命令模式通过封装请求,实现了调用者和接收者的解耦,但同时也增加了类的数量。在简单的场景中,可能会显得过于繁琐。
  4. 命令的参数化: 可以通过在命令接口中添加参数,实现对命令的参数化。这样,可以灵活地传递不同的参数给具体的命令对象。
  5. 多命令组合: 可以将多个命令组合成一个复合命令,实现对一系列操作的执行。
  6. 通用性考虑: 在实际应用中,需要根据具体情况权衡命令模式的使用,确保它能够简化系统结构,提高系统灵活性,并符合系统的维护和扩展需求。

优点:

  1. 松耦合: 命令模式将调用者和接收者解耦,使得系统中的对象更加独立。调用者无需了解接收者的具体实现,仅需要知道如何使用命令对象即可。
  2. 容易扩展: 新的命令类可以很容易地添加到系统中,而无需修改现有的代码。这使得系统更容易扩展和维护。
  3. 支持撤销和重做: 命令模式通常支持撤销和重做操作,使得系统能够回滚先前的命令,提高系统的可维护性。
  4. 支持事务: 命令模式可以用于实现事务,即一系列操作要么都成功,要么都失败。这对于需要确保一系列相关操作的一致性的场景非常有用。
  5. 支持命令的排队、队列和日志: 由于命令被封装为对象,可以轻松地将命令对象放入队列中,实现对命令的排队和执行。同时,也可以将命令对象记录到日志中,实现对系统操作的记录和重放。

缺点:

  1. 类膨胀: 每个具体命令都需要一个对应的命令类,可能会导致类的数量增加,系统变得复杂。
  2. 不适合复杂场景: 在某些复杂的场景中,可能需要更为灵活的设计模式,命令模式并不总是适用。

应用场景:

  1. 菜单和按钮操作: 在图形用户界面中,菜单和按钮的点击操作通常使用命令模式,将不同的操作封装为命令对象。
  2. 多级撤销和重做: 当需要支持多级撤销和重做操作时,命令模式是一个常见的选择。
  3. 遥控器设计: 遥控器通常使用命令模式,每个按钮对应一个命令,从而实现对设备的控制。
  4. 任务调度和队列系统: 命令模式可以用于实现任务调度和队列系统,将命令对象放入队列中依次执行。
  5. 数据库事务管理: 在数据库系统中,命令模式可以用于管理事务的执行和回滚,确保一系列数据库操作的一致性。

总体而言,命令模式适用于需要解耦调用者和接收者、支持撤销和重做、支持命令的排队、队列和日志等场景。在合适的场景下,命令模式可以提高系统的灵活性和可维护性。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2天前
|
设计模式 Java API
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
Kotlin教程笔记(50) - 改良设计模式 - 工厂模式
|
2天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
|
2天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
16天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
9天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
27 9
|
8天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
21 2
|
18天前
|
设计模式 Java 开发者
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin的用户,推荐查看“简洁”系列教程。本文重点介绍迭代器模式,通过具体示例展示了如何在Kotlin中实现迭代器模式,包括使用Iterator、Iterable接口及重载iterator运算符的方法。
27 4
|
18天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文通过游泳运动员的案例,介绍策略模式及其在Kotlin中的改良应用,利用高阶函数简化代码结构,提高灵活性。
27 3
|
19天前
|
设计模式 Java 开发者
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
本教程详细讲解了Kotlin中的迭代器模式,包括如何通过实现Iterator和Iterable接口以及重载iterator运算符来实现可遍历的自定义集合。示例展示了如何创建一个图书集类,并通过不同方式使其支持遍历操作,适合希望深入了解Kotlin迭代器模式的开发者。
29 3
|
19天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
本教程详细讲解Kotlin语法,适合深入学习。对于快速掌握Kotlin,推荐“简洁”系列教程。本文特别介绍了观察者模式,包括使用Java API和Kotlin委托属性(如Delegates.observable)实现的方法,旨在帮助开发者更高效地实现和优化观察者模式的应用。
30 3