二十三种设计模式(十四)--命令模式

命令模式(Command)

当我们有一个功能完善的类VideoClass,能够实现视频转码, 视频缓存 等等实际功能.

此时调用者需要依赖用户输入的命令来执行VideoClass中的一个或几个方法函数.

直觉上, 我们会写一个switch-case语句来处理用户输入的命令, 并执行VideoClass中的对应方法, 简单的可以这么做, 但是当用户的命令批量输入, 且要我们记录用户输入的所有命令, 或者要将所有命令都队列化存储之后依次执行时, 我们需要将命令解耦出来, 封装成独立的类, 这就是命令模式.

假如我们有如下问题:

java 复制代码
public class CommandPattern {
    public static void main(String[] args) {
        String command = args[1]; // 用户输入的指令

        Robot robot = new Robot();
        Weapon weapon = new Weapon();

        // TODO:: 要实现用户批量输入的命令: 变身车辆-发射5次粒子炮-变身人类-发射3发子弹
        // TODO:: 延迟3分钟发射粒子炮

        switch (command) {
            case "car":
                robot.transToCar();
                // TODO:: 记录日志: 变身车辆
                // TODO:: 记录变身耗时
                // 其他操作...
            case "human":
                robot.transToHuman();
                // TODO:: 记录日志: 变身人类
                // TODO:: 记录变身耗时
                // 其他操作...
            case "bullet":
                weapon.fireBullet();
                // TODO:: 记录日志: 开火发射子弹
                // TODO:: 记录发射子弹数量
                // 其他操作...
            case "particle":
                weapon.fireParticleCannon();
                // TODO:: 记录日志: 开火发射粒子炮
                // TODO:: 记录发射粒子炮次数
                // 其他操作...
        }
    }
}

// 两个真正执行指令对应的功能的类
class Robot {
    public void transToCar() {
        System.out.println("[robot] 变身一辆车");
    }

    public void transToHuman() {
        System.out.println("[robot] 变身T800");
    }
}

class Weapon {
    public void fireBullet() {
        System.out.println("[武器] 发射子弹");
    }

    public void fireParticleCannon() {
        System.out.println("[武器] 发射粒子炮");
    }
}

代码中标注TODO的部分, 随着需求的扩展, 会变得越来越臃肿

以下是针对以上问题, 命令模式的实现

定义命令的统一接口, 所有的命令类都依据接口实现功能

java 复制代码
// 命令模式的核心, 定义通用的命令接口
interface Command {
    // 执行命令
    void execute();
    // 获取命令名称(用于日志)
    String getCommandName();
}

// 命令一:变身车辆(绑定Robot接收者)
class TransToCarCommand implements Command {
    private final Robot robot;
    // 附加:记录耗时、日志
    private long startTime;
    private long endTime;

    public TransToCarCommand(Robot robot) {
        this.robot = robot;
    }

    @Override
    public void execute() {
        startTime = System.currentTimeMillis();
        // 执行接收者的核心逻辑
        robot.transToCar();
        endTime = System.currentTimeMillis();
        // 附加逻辑:记录日志、耗时
        log();
    }

    @Override
    public String getCommandName() {
        return "变身车辆";
    }

    private void log() {
        System.out.printf("[日志] 命令:%s,执行时间:%s,耗时:%dms%n",
                getCommandName(), new Date(), endTime - startTime);
    }
}

// 命令二:发射子弹(支持参数:发射次数)
class FireBulletCommand implements Command {
    private final Weapon weapon;
    private final int times; // 发射次数(参数封装)
    private long startTime;
    private long endTime;

    public FireBulletCommand(Weapon weapon, int times) {
        this.weapon = weapon;
        this.times = times;
    }

    @Override
    public void execute() {
        startTime = System.currentTimeMillis();
        // 执行带参数的逻辑
        for (int i = 0; i < times; i++) {
            weapon.fireBullet();
        }
        endTime = System.currentTimeMillis();
        log();
    }

    @Override
    public String getCommandName() {
        return "发射子弹";
    }

    private void log() {
        System.out.printf("[日志] 命令:%s,次数:%d,执行时间:%s,耗时:%dms%n",
                getCommandName(), times, new Date(), endTime - startTime);
    }
}

// 命令三 ...
// 命令四 ...
// 命令五 ...
// 命令随着功能类的更新可以无限扩展

由于单独命令都进行了封装, 因此可以将命令队列化批量操作, 可以将命令记录日志

可以支持更灵活的操作

于是, 下面这个类实现将命令存储进队列后依次执行的功能

java 复制代码
// 请求者:命令调用器(管理命令队列,批量执行)
class CommandInvoker {
    // 命令队列(存储所有命令,支持批量执行)
    private final List<Command> commandQueue = new ArrayList<>();

    // 添加命令到队列
    public void addCommand(Command command) {
        commandQueue.add(command);
    }

    // 执行队列中的所有命令
    public void executeAll() {
        System.out.println("\n===== 开始执行命令队列 =====");
        for (Command command : commandQueue) {
            command.execute();
        }
        System.out.println("===== 命令队列执行完成 =====\n");
    }

    // 清空队列
    public void clearQueue() {
        commandQueue.clear();
    }
}

真正执行指令功能的类不修改

java 复制代码
import java.util.Date;
import java.util.ArrayList;
import java.util.List;

public class CommandPattern {
    public static void main(String[] args) {
        // 1. 创建接收者(真正干活的对象)
        Robot robot = new Robot();
        Weapon weapon = new Weapon();

        // 2. 创建请求者(命令调用器,管理队列)
        CommandInvoker invoker = new CommandInvoker();

        // 3. 客户端组装命令(批量命令:变身车辆-延迟3分钟发射5次粒子炮-变身人类-发射3发子弹)
        invoker.addCommand(new TransToCarCommand(robot)); // 变身车辆
        invoker.addCommand(new FireBulletCommand(weapon, 3)); // 发射3发子弹

        // 4. 执行所有命令(请求者负责执行,客户端无需关心细节)
        invoker.executeAll();
    }
}

// 两个指令接收者, 真正执行指令对应的功能
class Robot {
    public void transToCar() {
        System.out.println("[robot] 变身一辆车");
    }

    public void transToHuman() {
        System.out.println("[robot] 变身T800");
    }
}

class Weapon {
    public void fireBullet() {
        System.out.println("[武器] 发射子弹");
    }

    public void fireParticleCannon() {
        System.out.println("[武器] 发射粒子炮");
    }
}

执行结果:

复制代码
===== 开始执行命令队列 =====
[robot] 变身一辆车
[日志] 命令:变身车辆,执行时间:Thu Dec 18 14:21:38 CST 2025,耗时:0ms
[武器] 发射子弹
[武器] 发射子弹
[武器] 发射子弹
[日志] 命令:发射子弹,次数:3,执行时间:Thu Dec 18 14:21:38 CST 2025,耗时:0ms
===== 命令队列执行完成 =====

命令模式只解决一件事:

把"要做什么"封装成对象

其他能力(队列 / 日志 / 撤销)都是围绕这个对象自然搭建出来的

当看到代码里出现下面的逻辑:

复制代码
if (cmd == A) doA();
if (cmd == B) doB();

👉 90% 情况:该考虑命令模式了

相关推荐
程序员zgh2 小时前
C++常用设计模式
c语言·数据结构·c++·设计模式
一起养小猫2 小时前
《Java数据结构与算法》第四篇(三)二叉树遍历详解_CSDN文章
java·开发语言·数据结构
少许极端2 小时前
算法奇妙屋(十九)-子序列问题(动态规划)
java·数据结构·算法·动态规划·子序列问题
小满、2 小时前
RabbitMQ:AMQP 原理、Spring AMQP 实战与 Work Queue 模型
java·rabbitmq·java-rabbitmq·spring amqp·amqp 协议·work queue
萧曵 丶2 小时前
Java Stream 实际用法详解
java·stream·lambda
dvlinker2 小时前
动态代理技术实战测评—高效解锁Zillow房价历史
android·java·数据库
喵手2 小时前
JVM 基础知识:深入理解 Java 的运行时环境!
java·jvm·jvm基础·java运行环境
简烦2 小时前
外层事务的 afterCommit 中调用内层事务方法时,内层事务的 TransactionSynchronization 注册失败 / 不执行
java·spring
峥嵘life2 小时前
Android16 EDLA 认证BTS测试Failed解决总结
android·java·linux·运维·学习