Java设计模式再相识 (十五)——命令模式

目录

  • 命令模式
    • 命令模式的结构
    • 命令模式的实际应用场景
    • 示例
    • 命令模式的优缺点
      • 优点
      • 缺点
  • 总结

命令即Command,是我们在计算机中经常听到的词汇,命令能够让计算机明确自己的职责。现实生活中,命令模式的例子有很多,例如电视遥控器发送的红外码就可以理解成命令模式,遥控器向电视发送请求,电视根据具体请求进行处理。这就是典型的命令模式解耦的例子。

命令模式

命令模式(Command)的定义:将发送命令者封装成对象(类比电视遥控器),使发送请求的职责与处理请求的职责分离,即解耦,使两者通过命令进行通讯。这样,当需要管理这些对象的工作情况,就可以直接管理这些命令实例的集合,而无需修改具体的处理逻辑。命令模式能够方便对象之间的调用,提高系统的可拓展性。

命令模式的结构

命令模式主要包含以下结构:

  • 抽象命令(Command):定义了命令的接口和执行命令的抽象函数execute()
  • 具体命令(ConcreteCommand):实现了抽象命令中的所有方法,并管理一个接收者(Receiver)对象,通过调用接收者(Receiver)的功能来完成命令的具体操作。
  • 接收者(Receiver):接收者负责接收并执行命令的具体操作,是业务逻辑的处理类。
  • 调用者(Invoker):调用者即发送请求的类,它管理着命令对象,并通过调用命令对象来执行请求,请注意,它不能直接访问接收者。

命令模式的实际应用场景

  • 与硬件的串口通讯:在于硬件进行串口通信时,常常会有大量命令需要传输,如果使用if else语句进行判断后传输给硬件,会造成代码杂乱无章,难以拓展。使用命令模式将重要指令进行封装,能够将命令的发送和处理进行逻辑分离,有利于系统的解耦。
  • 复杂文本编辑器的操作:文本编辑器通常需要各种功能,例如最基本的保存、复制、粘贴操作。如果体量很小,我们当然无需使用命令模式来增加系统的复杂度。但通常实际项目的体量往往比我们设想的大得多,所以我们需要提前考虑项目的拓展。例如文本编辑器还有撤销、重做等功能。这个时候使用命令模式就是必要的。

注:在使用命令模式时,我们还可以将一系列命令以java.util.List即列表的形式进行保存,这样我们还可以轻松维护工作的历史记录,只需要维护List实例即可。在需要执行历史命令时,只需要从实例列表中取出。

示例

在本文示例中,我们将模拟使用命令模式实现一个股票交易买卖Demo。股票的操作有许多种,Demo实现股票的买和卖两种操作,使用命令模式完成,方便后续其他操作模式的拓展。

首先,我们创建一个抽象命令接口。

Order.java

package com.yeliheng.command;


/**
 * 抽象命令
 */
public interface Order {
    void execute();
}

在抽象命令中,具有一个execute()方法,待会的具体命令将实现这个execute()方法。

接着,我们需要定义一个命令的接收者,即负责处理命令的具体逻辑操作。

StockCommandReceiver.java

package com.yeliheng.command;

/**
 * 命令接收者,处理具体的买卖逻辑
 */
public class StockCommandReceiver {

    private final String stockCode = "123456"; //股票代码

    private final int quantity = 100; //买入数量

    public void buy() {
        System.out.println("[买入] 股票代码: " + stockCode + " 数量(股): " + quantity);
    }

    public void sell() {
        System.out.println("[卖出] 股票代码: " + stockCode + " 数量(股): " + quantity);
    }
}

在这个类中,我们定义了两种操作的方法,分别是buy()sell()。在两个方法中分别输出股票代码和股票数量。

然后我们就可以来实现具体命令了。

BuyCommand.java

package com.yeliheng.command;

/**
 * 具体命令
 */
public class BuyCommand implements Order{

    private StockCommandReceiver receiver;

    public BuyCommand(StockCommandReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.buy();
    }
}

BuyCommand类实现了Order订单的execute()方法,并且管理了一个命令接收者实例,方便调用者Invoker进行调用。

SellCommand与BuyCommand同理,具体代码如下:

SellCommand.java

package com.yeliheng.command;

/**
 * 具体命令
 */
public class SellCommand implements Order{

    private StockCommandReceiver receiver;

    public SellCommand(StockCommandReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.sell();
    }
}

最后,我们要实现一个命令的调用者即Invoker类,该类中含有下单和处理订单的具体逻辑。并管理了一个命令列表,方便记录历史。

Invoker.java

package com.yeliheng.command;

import java.util.ArrayList;
import java.util.List;

/**
 * 调用者
 */
public class Invoker {

    private List<Order> orderList = new ArrayList<>();

    //下单
    public void takeOrder(Order order) {
        orderList.add(order);
    }

    //处理订单
    public void handleOrders() {
        for(Order order : orderList) {
            order.execute();
        }
        orderList.clear();
    }

}

在Invoker类中,我们实现了两个方法,分别是takeOrder()handleOrders()takeOrder()方法会将所有订单加入到订单列表中。而handleOrders()方法将遍历所有订单,负责请求具体命令,达到订单处理的效果。

完成了命令模式的基本结构后,我们在main函数中进行使用:

Main.java

package com.yeliheng.command;

public class Main {
    public static void main(String[] args) {

        //注册接收者
        StockCommandReceiver receiver = new StockCommandReceiver();
        //注册命令
        BuyCommand buyCommand = new BuyCommand(receiver);
        SellCommand sellCommand = new SellCommand(receiver);

        //下单
        Invoker invoker = new Invoker();
        invoker.takeOrder(buyCommand);
        invoker.takeOrder(sellCommand);

        //处理订单
        invoker.handleOrders();
    }
}

在Main类中,我们首先实例化了一个接受者,再实例化具体命令,将具体命令交付给调用者实例进行管理,并执行具体业务逻辑。

最终程序的输出结果如下图所示:

output.png

这样就完成了一个完整命令模式的示例。

命令模式的优缺点

优点

  • 命令模式通过抽象接口来降低系统的耦合度。在命令的发送与具体业务的执行上进行了分离,有利于系统的解耦和后期的拓展。当有具体命令产生时,只需要增加新的具体命令类即可,无需修改命令处理的具体代码,符合开闭原则。
  • 可与多种设计模式结合使用,实现强大的事件处理系统。

注:命令也可认为是一种特殊的事件(event),合理使用命令模式能够很好地实践“事件驱动编程”的思想。

缺点

  • 过度使用命令模式可能会造成具体命令类大幅增长,从而提升系统的复杂度与维护难度。

总结

本文通过股票交易系统示例进行命令模式的讲解。命令模式在Web开发中可能用得并不多,但作为一个基础的设计模式,我们也必须熟练掌握。它在底层框架上和客户端上的应用较为广泛,掌握命令模式有助于深入理解开源框架。

本文示例的完整源代码参见:Github

Java设计模式行为型模式
2025 © Yeliheng的技术小站 版权所有