文章目录
- [0. 个人感悟](#0. 个人感悟)
- [1. 概念](#1. 概念)
- [2. 适配场景](#2. 适配场景)
-
- [2.1 适合的场景](#2.1 适合的场景)
- [2.2 常见场景举例](#2.2 常见场景举例)
- [3. 实现方法](#3. 实现方法)
-
- [3.1 实现思路](#3.1 实现思路)
- [3.2 UML类图](#3.2 UML类图)
- [3.3 代码示例](#3.3 代码示例)
- [4. 优缺点](#4. 优缺点)
-
- [4.1 优点](#4.1 优点)
- [4.2 缺点](#4.2 缺点)
- [5. 源码分析](#5. 源码分析)
0. 个人感悟
- 命令模式核心是将请求或者操作封装成对象。那么就可以基于这些对象进行额外操作,比如队列记录、日志、撤销恢复等
- 实际工作中,对于任务队列其实已经有很多成熟的框架,不过万变不离其宗,理解命令模式,对于其它知识(比如三方件、架构)的学习还是很有帮助的
1. 概念
英文定义 (《设计模式:可复用面向对象软件的基础》)
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or long requests, and support undoable operations.
中文翻译
将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
理解
- 请求封装:将"做什么"(操作)和"谁来做"(执行者)分离
- 参数化:可以像传递参数一样传递命令对象
- 延迟执行:命令可以在创建后的某个时间点执行
- 可撤销/重做:通过记录命令历史实现操作回退
2. 适配场景
2.1 适合的场景
- 解耦调用者和接收者:需要将请求的发起者和执行者解耦时
- 支持撤销/重做:需要实现操作的撤销和重做功能
- 任务队列/日志:需要将请求排队、记录日志或延迟执行
2.2 常见场景举例
- 遥控器控制家电:不同按钮触发不同设备的不同操作
- 餐厅点餐系统:订单作为命令,厨师作为接收者
- 文本编辑器:撤销/重做功能
- 线程池任务调度:将任务封装为命令对象
- 游戏控制:玩家输入映射到游戏角色的不同动作
3. 实现方法
3.1 实现思路
- 定义命令接口 :声明执行命令的抽象方法(通常包含
execute()和undo()) - 创建具体命令类:实现命令接口,关联接收者对象
- 定义接收者类:实际执行操作的对象
- 创建调用者/请求者类:持有命令对象并触发执行
- 客户端组装:创建命令对象并设置给调用者
3.2 UML类图
![[命令模式_UML.png]]

角色说明:
- Command(命令接口):声明执行操作的接口
- ConcreteCommand(具体命令):绑定接收者和动作
- Receiver(接收者):知道如何执行请求的具体操作
- Invoker(调用者):持有命令对象并触发执行
- Client(客户端):创建具体命令并设置接收者
3.3 代码示例
背景: 遥控器控制家电,可以遥控灯、电视灯家具
命令接口:
java
public interface Command {
/**
* @description 执行
* @author bigHao
* @date 2026/1/20
**/
void execute();
/**
* @description 撤销
* @author bigHao
* @date 2026/1/20
**/
void undo();
}
灯的接受者和具体命令:
java
public class LightReceiver {
/**
* @description 开灯
* @author bigHao
* @date 2026/1/20
**/
public void on() {
System.out.println("开灯了");
}
/**
* @description 关灯
* @author bigHao
* @date 2026/1/20
**/
public void off() {
System.out.println("关灯了");
}
}
public class LightOnCommand implements Command {
private LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.on();
}
@Override
public void undo() {
lightReceiver.off();
}
}
public class LightOffCommand implements Command {
private LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
电视接受者和命令:
java
public class TVReceiver {
/**
* @description 开机
* @author bigHao
* @date 2026/1/20
**/ public void on() {
System.out.println("电视开了");
}
/**
* @description 关机
* @author bigHao
* @date 2026/1/20
**/ public void off() {
System.out.println("电视关了");
}
}
public class TVOnCommand implements Command {
private TVReceiver receiver;
public TVOnCommand(TVReceiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.on();
}
@Override
public void undo() {
receiver.off();
}
}
public class TVOffCommand implements Command {
private TVReceiver receiver;
public TVOffCommand(TVReceiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.off();
}
@Override
public void undo() {
receiver.on();
}
}
遥控器:
java
public class RemoteController {
public static final int INIT_COMMAND_NUM = 5;
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand;
public RemoteController() {
onCommands = new Command[INIT_COMMAND_NUM];
offCommands = new Command[INIT_COMMAND_NUM];
for (int i = 0; i < INIT_COMMAND_NUM; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
public void setOnCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void on(int no) {
onCommands[no].execute();
// 记录当前操作
undoCommand = onCommands[no];
}
public void off(int no) {
offCommands[no].execute();
// 记录当前操作
undoCommand = offCommands[no];
}
public void undo() {
undoCommand.undo();
}
}
测试:
java
public class Client {
public static final int LIGHT_NO = 0;
public static final int TV_NO = 1;
static void main() {
RemoteController remoteController = new RemoteController();
LightReceiver lightReceiver = new LightReceiver();
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 按键0是灯开关
remoteController.setOnCommand(LIGHT_NO, lightOnCommand, lightOffCommand);
System.out.println("=== 按下开灯键位 ===");
remoteController.on(LIGHT_NO);
System.out.println("=== 按下关灯键位 ===");
remoteController.off(LIGHT_NO);
System.out.println("=== 按下撤销键 ===");
remoteController.undo();
TVReceiver tvReceiver = new TVReceiver();
TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
// 按键1是灯开关
remoteController.setOnCommand(TV_NO, tvOnCommand, tvOffCommand);
System.out.println("=== 按下开机键位 ===");
remoteController.on(TV_NO);
System.out.println("=== 按下关机键位 ===");
remoteController.off(TV_NO);
System.out.println("=== 按下撤销键 ===");
remoteController.undo();
}
}
输出:
=== 按下开灯键位 ===
开灯了
=== 按下关灯键位 ===
关灯了
=== 按下撤销键 ===
开灯了
=== 按下开机键位 ===
电视开了
=== 按下关机键位 ===
电视关了
=== 按下撤销键 ===
电视开了
4. 优缺点
4.1 优点
高内聚低耦合:
- 调用者与接收者解耦:调用者无需知道接收者的具体实现
- 命令对象内聚性高:每个命令专注于一个具体操作
复用性与可扩展性:
- 易于扩展新命令:只需实现Command接口
- 命令可复用:同一命令可在不同上下文中使用
维护性:
- 易于维护和修改:修改具体操作只需修改对应命令类
- 支持宏命令:可将多个命令组合成复杂操作
稳定性与可靠性:
- 支持事务:可批量执行命令并支持回滚
- 支持撤销/重做:通过命令历史记录实现
4.2 缺点
复杂性增加:
- 类数量增多:每个命令都需要一个具体类
- 系统复杂度提高:增加了间接层次
性能开销:
- 内存占用:每个命令都需要创建对象
- 执行效率:间接调用可能比直接调用稍慢
5. 源码分析
Java标准库中Runnable相关实现是简化的命令模式
java.lang.Runnable接口
java
// Runnable就是命令接口
public interface Runnable {
public abstract void run(); // execute()方法
}
// Thread作为Invoker
Thread thread = new Thread(() -> System.out.println("Running command"));
thread.start(); // 触发命令执行
角色分析:
- Command(命令接口):Runnable接口,它定义了run()方法,相当于命令模式中的执行方法。
- ConcreteCommand(具体命令) :实现了
Runnable接口的类,例如我们通过匿名内部类、Lambda表达式或者具体类实现的run方法中的具体逻辑。 - Receiver(接收者):可以是Ru实际执行操作的对象。通常Runnable的实现会调用其他对象(接收者)的方法。
- Invoker(调用者/请求者) :调用命令的对象。在Java中,
Thread类就是一个典型的调用者,它接收一个Runnable(命令)并在适当的时机调用其run方法。
参考: