← 返回题目列表

什么是命令模式?它解决什么问题?

高频 简单 第 2 / 25 题 更新于 2026/07/28
命令模式行为型模式请求对象化解耦设计模式

简化版

命令模式是把一次请求封装成一个命令对象,让请求发起者不直接调用真正的执行者。它主要解决调用者和执行者耦合过紧的问题,并支持排队、日志、撤销、重做、异步执行等扩展能力。

详细版

命令模式的核心思想是:把“要做什么”封装成对象。

普通调用里,调用者通常直接写:

receiver.action();

用了命令模式后,调用者只需要执行命令:

command.execute();

真正由谁执行、怎么执行、能不能撤销,都被封装在命令对象内部。

命令模式常用于这些场景:

  • 菜单按钮、快捷键、遥控器等 UI 操作;
  • 任务队列和异步任务;
  • 撤销、重做;
  • 宏命令;
  • 操作日志和审计;
  • 事务型操作和补偿动作。

它的价值不是让简单调用变复杂,而是在“请求需要被存储、传递、排队、撤销或统一调度”时,把请求变成可管理的对象。

完整版教学

一、为什么要把请求封装成对象

在普通代码里,一次调用通常由调用方直接发起:

class Button {
    private Light light;

    public void click() {
        light.on();
    }
}

这段代码的问题是 Button 直接依赖 Light,按钮只能控制灯。如果明天按钮要控制风扇、空调、音响,就要修改按钮代码。

命令模式把“打开灯”这件事封装成对象:

interface Command {
    void execute();
}

class LightOnCommand implements Command {
    private final Light light;

    LightOnCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.on();
    }
}

按钮只依赖 Command

class Button {
    private Command command;

    public void click() {
        command.execute();
    }
}

这样按钮不关心命令背后控制的是灯、风扇还是别的设备。

二、命令模式解决的不是“调用”本身

如果只是把一行方法调用换成一个类,命令模式看起来会显得啰嗦。

它真正解决的是:请求需要被当成对象来管理。

对象可以被:

  • 保存到列表里;
  • 放进队列里;
  • 延迟执行;
  • 记录日志;
  • 传递给其他模块;
  • 组合成批量操作;
  • 执行失败后补偿;
  • 反向执行用于撤销。

这就是“请求对象化”的价值。

三、命令模式的关键解耦点

命令模式把系统拆成三层:

Invoker 调用命令
Command 封装请求
Receiver 真正执行业务

调用者只知道 Command 接口,不知道具体执行者。

例如遥控器只知道按钮绑定了一个命令,不知道命令内部是开电视、开灯还是调空调温度。

四、命令模式为什么适合撤销重做

撤销重做要求系统不仅知道“执行了什么”,还要知道“怎么反向操作”。

命令对象可以同时提供:

void execute();
void undo();

执行后把命令放入历史栈:

执行命令 -> 入撤销栈
撤销命令 -> 从撤销栈弹出并调用 undo
重做命令 -> 从重做栈弹出并调用 execute

如果没有命令对象,系统就很难统一管理各种不同操作的撤销逻辑。

五、命令模式不是每个业务都要用

简单 CRUD 直接调用 service 方法通常更清晰。

只有当请求具有“可管理性需求”时,命令模式才值得引入,例如:

  • 请求要排队;
  • 请求要异步执行;
  • 请求要撤销;
  • 请求要记录日志;
  • 请求要统一调度;
  • 请求要组合成批处理。

如果没有这些需求,硬套命令模式会增加类数量和理解成本。

六、常见误区与追问

是否需要命令模式,可以用时间和空间解耦来判断。调用者在 T0 创建“发送报表”命令,调度器在 T1 执行,重试器在 T2 再次处理;三者不必了解报表服务的具体接口。请求变成对象后,才能被保存、传输、组合和记录,但也必须承担参数冻结与失败语义。

检查维度判定依据
直接调用请求随方法返回而结束
命令对象请求可跨时间保存和调度

记忆钩子:把动词连同参数装进对象,请求才有了可管理的生命周期。

  • 误区:命令模式只是给方法调用包一层。 只有封装后获得解耦、排队、撤销等能力时,这层抽象才有意义。
  • 追问:为什么它属于行为型模式? 它关注对象之间如何传递和执行请求,而不是创建对象或组织静态结构。
  • 误区:命令对象必须自己完成所有业务。 它通常委托 Receiver,避免命令类演化成另一个业务服务。
  • 追问:命令参数何时确定? 通常在创建命令时冻结,以保证延迟执行和重放时语义稳定。
  • 追问:它解决调用者和谁的耦合? 主要隔离调用者与具体执行者、方法和参数组织方式。

七、加强记忆

命令模式的核心是把请求封装成命令对象,让调用者只负责触发命令,真正的业务执行交给接收者。它的价值在于让请求可以被保存、排队、组合、撤销、重做和统一调度,而不是单纯把方法调用包一层。