← 返回题目列表

命令模式有哪些角色?调用流程是什么?

高频 简单 第 1 / 25 题 更新于 2026/07/28
命令模式InvokerReceiverCommand调用流程

简化版

命令模式通常有四个角色:Command 定义命令接口,ConcreteCommand 封装具体请求,Receiver 执行业务动作,Invoker 触发命令。调用流程是客户端创建命令并绑定接收者,Invoker 持有命令,用户触发后 Invoker 调用 execute()

详细版

命令模式的典型角色包括:

角色作用
Command抽象命令接口,通常定义 execute()
ConcreteCommand具体命令,持有 Receiver,并调用 Receiver 的业务方法
Receiver真正完成业务动作的对象
Invoker请求触发者,只负责调用命令
Client负责组装命令、接收者和调用者

典型调用流程:

Client 创建 Receiver
Client 创建 ConcreteCommand,并传入 Receiver
Client 把 Command 设置给 Invoker
Invoker 在合适时机调用 command.execute()
ConcreteCommand 调用 Receiver 完成真实业务

命令模式的重点是让 Invoker 和 Receiver 解耦。Invoker 不知道业务怎么做,只知道执行命令。

完整版教学

一、Command:统一命令入口

命令接口通常很简单:

interface Command {
    void execute();
}

如果要支持撤销,可以扩展为:

interface Command {
    void execute();
    void undo();
}

Command 的作用是给调用方一个统一入口。无论背后命令多复杂,Invoker 都只需要调用 execute()

二、ConcreteCommand:把请求和执行者绑定起来

具体命令负责封装一次请求。

class LightOnCommand implements Command {
    private final Light light;

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

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

它通常会持有 Receiver,然后在 execute() 里调用 Receiver 的方法。

注意,ConcreteCommand 不是业务实体本身,它更像“操作单”或“任务单”。

三、Receiver:真正干活的人

Receiver 才是真正执行业务逻辑的对象。

class Light {
    void on() {
        System.out.println("开灯");
    }

    void off() {
        System.out.println("关灯");
    }
}

命令对象不应该承载所有复杂业务。真实业务逻辑仍然应该放在领域对象或服务对象里,命令负责调度和封装请求。

四、Invoker:只负责触发命令

Invoker 是请求触发者,例如按钮、菜单项、遥控器、任务调度器。

class RemoteButton {
    private Command command;

    void setCommand(Command command) {
        this.command = command;
    }

    void press() {
        command.execute();
    }
}

Invoker 不需要知道 Light,也不需要知道 on() 方法。它只知道执行命令。

五、Client:负责装配关系

Client 负责把对象组装起来:

Light light = new Light();
Command command = new LightOnCommand(light);

RemoteButton button = new RemoteButton();
button.setCommand(command);
button.press();

在 Spring 项目里,这种装配可能由容器完成,也可能通过工厂或配置表完成。

六、调用流程背后的解耦意义

命令模式最重要的解耦是:

Invoker 不依赖 Receiver
Invoker 只依赖 Command 抽象
ConcreteCommand 负责连接 Command 和 Receiver

这样 Invoker 可以绑定不同命令:

  • 开灯命令;
  • 关灯命令;
  • 播放音乐命令;
  • 执行批处理命令。

Invoker 的代码完全不用改。

七、常见误区与追问

角色划分的验收可以沿一条调用链检查依赖方向。客户端装配1个 Invoker、1个具体命令和1个 Receiver 后,运行期应由 Invoker 只调用 execute(),再由命令转给 Receiver。若 Invoker 直接识别 Receiver 类型,或者 Receiver 反向依赖界面按钮,角色边界已经被破坏。

检查维度判定依据
Invoker保存并触发抽象命令
ConcreteCommand绑定参数与 Receiver

记忆钩子:Client 负责“组装”,Invoker 负责“按下”,Receiver 负责“执行”。

  • 误区:Client 是最终用户。 模式角色中的 Client 是创建并装配对象关系的代码,不一定是 UI 用户。
  • 追问:Invoker 能否保存多个命令? 可以,菜单、快捷键和调度器都常维护命令集合。
  • 误区:Receiver 必须只有一个。 一个宏命令可以协调多个 Receiver,但要避免把全部业务塞进命令。
  • 追问:ConcreteCommand 为什么需要 Receiver? 它把抽象请求翻译为具体业务调用,从而隔离触发者与执行者。
  • 追问:怎样测试角色解耦? 用假命令验证 Invoker 只触发接口,再单测具体命令是否正确调用 Receiver。

八、加强记忆

命令模式的角色可以记成“调用者按按钮,命令对象接单,接收者干活”。Invoker 只触发命令,ConcreteCommand 封装请求并调用 Receiver,Receiver 负责真实业务,Client 负责把它们装配起来。