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

命令模式(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% 情况:该考虑命令模式了

相关推荐
reddingtons14 小时前
【游戏宣发】PS “生成式扩展”流,30秒无损适配全渠道KV
游戏·设计模式·新媒体运营·prompt·aigc·教育电商·游戏美术
李慕婉学姐15 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆16 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin17 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model200517 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉17 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国17 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_9418824817 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈18 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_9918 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc