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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 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日志并进行多维度分析。
目录
相关文章
|
5天前
|
设计模式 Java
【设计模式系列笔记】责任链模式
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。每个处理者都有一个对下一个处理者的引用。责任链模式常用于处理请求的场景,例如在一个请求需要经过多个步骤或者多个对象来处理的情况下。
23 0
|
5天前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
42 1
|
5天前
|
设计模式 存储 Java
【搞懂设计模式】命令模式:从遥控器到编程的妙用!
【搞懂设计模式】命令模式:从遥控器到编程的妙用!
7 0
|
5天前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
11 1
|
5天前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
26 4
|
5天前
|
设计模式 JavaScript 前端开发
vue的设计模式_笔记
vue的设计模式_笔记
16 0
|
5天前
|
设计模式 算法 编译器
【设计模式系列笔记】访问者模式
访问者模式是一种行为设计模式,旨在将算法与对象结构分离,使得能够在不修改元素类的前提下定义新的操作。这一模式的核心思想是在元素类中添加一个接受访问者的方法,从而实现在不同元素上执行不同操作的能力。
33 0
|
5天前
|
设计模式 SQL 算法
【设计模式系列笔记】模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并允许子类在不改变该算法结构的情况下重新定义算法的某些步骤。这种模式属于行为型模式,它通过将算法的不同部分封装在不同的方法中,从而使子类能够在不改变算法结构的前提下定制算法的某些步骤。
32 0
|
5天前
|
设计模式 算法 Java
【设计模式系列笔记】策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
26 0
|
5天前
|
设计模式 Java
【设计模式系列笔记】状态模式
在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。
25 0