- 命令模式:它是什么,解决了什么问题。
- 队列:它是什么,核心特点是什么。
- 命令模式 + 队列:为什么它们是天作之合,以及如何实现。
命令模式
命令模式是一种行为型设计模式,它的核心思想是将“请求”封装成对象。

核心思想
想象一下你在餐厅点餐,你(客户端)不需要直接冲进厨房告诉厨师(接收者)如何做菜(具体操作),你只需要告诉服务员(调用者)你要什么菜(命令),服务员把你的菜单(命令对象)递给厨房,厨房根据菜单执行相应的操作。
在这个比喻中:
- 客户端:你,发起请求的人。
- 调用者:服务员,接收请求并调用命令执行。
- 命令:菜单,封装了“做什么菜”和“谁来做”的信息。
- 接收者:厨师,真正执行操作的人。
命令模式的优点
- 解耦:调用者(服务员)完全不知道接收者(厨师)是谁,也不知道具体怎么做,它只和命令对象(菜单)打交道,这使得系统各部分之间的耦合度大大降低。
- 可扩展性:可以非常方便地添加新的命令,比如餐厅推出了新菜,只需要创建一个新的“新菜菜单”命令类,而不需要修改服务员或厨师的代码。
- 支持撤销和重做:因为命令对象封装了操作,我们可以很容易地为命令对象添加
undo()方法,实现撤销功能。 - 支持延迟执行和日志记录:命令对象可以被存储、传递,然后在需要的时候才执行。
经典结构
- Command (命令接口): 声明执行操作的接口。
- ConcreteCommand (具体命令): 实现了 Command 接口,将一个接收者对象绑定到一个动作,它调用接收者相应的操作来执行请求。
- Client (客户端): 创建具体命令对象,并设置其接收者。
- Invoker (调用者): 要求命令执行请求。
- Receiver (接收者): 知道如何与一个请求相关的操作。
队列
队列是一种先进先出 的数据结构,它就像现实生活中的排队买票,第一个排队的人第一个买到票,离开队列。
核心特点
- 先进先出: 元素加入队列的一端(队尾),从另一端(队头)移除。
- 两个基本操作:
enqueue(入队): 将一个元素添加到队尾。dequeue(出队): 从队头移除并返回一个元素。
- 任务缓冲: 队列最常用于“缓冲”任务,当任务的生产速度和消费速度不一致时,队列可以起到平滑、削峰填谷的作用。
命令模式 + 队列 = 命令队列
我们把这两个强大的概念结合起来。命令队列就是将多个“命令对象”放入一个队列中,然后由一个或多个“消费者”按顺序地、一个接一个地取出并执行这些命令。

为什么它们是天作之合?
命令模式封装了“做什么”,而队列提供了“如何有序地处理”,结合后,我们得到了一个任务处理系统,它具备以下所有优点:
-
完全解耦:
- 生产者(产生任务的代码)只需要把命令对象
enqueue到队列中,然后就不用管了,它完全不知道是谁、在什么时候、如何执行这个任务。 - 消费者(执行任务的线程/进程)只需要从队列中
dequeue命令并执行,它完全不知道任务是从哪里来的,也不知道下一个任务是什么。 - 接收者(真正干活的对象)甚至不知道自己的操作是被命令模式封装,又被队列调度了。
- 生产者(产生任务的代码)只需要把命令对象
-
削峰填谷:
- 这是最核心的价值之一,假设一个Web应用在短时间内收到了大量用户请求(比如秒杀活动),如果每个请求都立即同步处理,服务器可能会瞬间崩溃。
- 通过引入命令队列,我们可以将这些请求快速地转换成命令对象并放入队列,由一个或几个固定的“消费者线程”以一个可控的速度从队列中取出并处理命令,这样,即使请求量暴增,队列会变长,但后端服务的处理压力是恒定的,从而保护了系统。
-
异步处理:
(图片来源网络,侵删)生产者将任务放入队列后可以立即返回响应,告诉用户“您的请求已收到,正在处理中”,而耗时的操作(如发送邮件、生成报表、处理视频)则交给队列后的消费者异步执行,极大地提升了系统的响应速度和吞吐量。
-
可扩展性:
- 当任务量变大,单个消费者处理不过来时,我们可以轻松地增加更多的消费者线程来并行处理队列中的任务,实现水平扩展。
- 想要支持新的功能?只需创建一个新的
ConcreteCommand类即可,无需修改生产者、消费者或队列的任何代码。
应用场景举例
-
任务队列/消息队列:
- 场景: 电商平台,用户下单后需要发送确认邮件、更新库存、记录日志等。
- 实现: 将“发送邮件”、“更新库存”等操作封装成不同的命令,用户的下单请求会将这些命令依次放入一个消息队列(如 RabbitMQ, Kafka),后台的多个 Worker(消费者)从队列中取任务执行。
-
GUI 中的撤销/重做:
- 场景: 文本编辑器,用户的每次操作(如输入、删除、格式化)都被封装成一个命令。
- 实现: 一个“撤销栈”本质上是一种特殊的命令队列(后进先出),每次用户执行操作,命令就被压入栈,点击“撤销”,就从栈顶弹出命令并执行其
undo()方法。
-
多线程任务调度:
- 场景: 一个需要处理大量计算密集型任务的应用。
- 实现: 主线程将所有任务封装成命令对象,放入一个任务队列,启动一个线程池,每个线程都作为消费者,从队列中获取任务并执行。
代码示例 (Python)
下面是一个简单的命令队列实现,模拟一个任务处理器。
import queue
import threading
import time
# 1. Receiver (接收者) - 真正执行任务的对象
class TextFile:
def save(self, content: str):
print(f"[接收者] 正在保存内容到文件: '{content}'...")
# 模拟耗时操作
time.sleep(2)
print(f"[接收者] 文件保存完成!")
# 2. Command (命令接口)
class Command:
def execute(self):
pass
# 3. ConcreteCommand (具体命令)
class SaveFileCommand(Command):
def __init__(self, receiver: TextFile, content: str):
self._receiver = receiver
self._content = content
def execute(self):
self._receiver.save(self._content)
# 4. Invoker (调用者) - 它也是队列的管理者和消费者
class TaskProcessor:
def __init__(self):
self._command_queue = queue.Queue()
print("[任务处理器] 已启动,等待任务...")
def add_task(self, command: Command):
"""生产者调用此方法添加任务"""
self._command_queue.put(command)
print(f"[生产者] 任务已添加到队列,当前队列大小: {self._command_queue.qsize()}")
def process_tasks(self):
"""消费者调用此方法处理队列中的任务"""
while True:
try:
# 从队列中获取命令,设置超时以避免无限阻塞
command = self._command_queue.get(timeout=1)
print(f"[消费者] 获取到任务,开始执行...")
command.execute()
self._command_queue.task_done() # 标记任务为完成
except queue.Empty:
print("[消费者] 队列为空,处理结束。")
break
# --- 客户端代码 ---
if __name__ == "__main__":
# 创建接收者
text_file = TextFile()
# 创建任务处理器 (消费者)
processor = TaskProcessor()
# 启动一个后台线程来处理任务
consumer_thread = threading.Thread(target=processor.process_tasks)
consumer_thread.start()
# 模拟客户端 (生产者) 添加任务
print("--- 模拟客户端添加任务 ---")
processor.add_task(SaveFileCommand(text_file, "第一份报告"))
time.sleep(0.5)
processor.add_task(SaveFileCommand(text_file, "第二份报告"))
time.sleep(0.5)
processor.add_task(SaveFileCommand(text_file, "第三份报告"))
# 等待所有任务被处理
# queue.join() 会阻塞,直到队列中的所有任务都被标记为完成
print("[主线程] 等待所有任务处理完毕...")
processor._command_queue.join()
print("[主线程] 所有任务处理完毕!")
# 停止消费者线程 (这里简单处理,实际中可能需要更优雅的停止机制)
# 可以设置一个特殊的“结束”命令
consumer_thread.join()
print("程序结束。")
输出结果分析
运行上述代码,你会看到类似这样的输出:
[任务处理器] 已启动,等待任务...
--- 模拟客户端添加任务 ---
[生产者] 任务已添加到队列,当前队列大小: 1
[消费者] 获取到任务,开始执行...
[接收者] 正在保存内容到文件: '第一份报告'...
[生产者] 任务已添加到队列,当前队列大小: 2
[生产者] 任务已添加到队列,当前队列大小: 3
[接收者] 文件保存完成!
[消费者] 获取到任务,开始执行...
[接收者] 正在保存内容到文件: '第二份报告'...
[接收者] 文件保存完成!
[消费者] 获取到任务,开始执行...
[接收者] 正在保存内容到文件: '第三份报告'...
[接收者] 文件保存完成!
[主线程] 等待所有任务处理完毕...
[消费者] 队列为空,处理结束。
[主线程] 所有任务处理完毕!
程序结束。
从输出中可以清晰地看到:
- 生产者快速地添加了3个任务,队列大小随之增加。
- 消费者独立运行,从队列中取出任务并执行,即使任务有耗时(2秒),生产者也不会被阻塞。
- 整个过程是异步和解耦的。
| 特性 | 命令模式 | 队列 | 命令队列 |
|---|---|---|---|
| 核心 | 将请求封装为对象 | 先进先出的数据结构 | 将命令对象放入队列 |
| 解决的问题 | 解耦调用者和接收者,支持撤销/重做 | 任务缓冲,削峰填谷 | 实现了异步、解耦、可扩展、高吞吐量的任务处理系统 |
| 关键角色 | Command, Invoker, Receiver | Producer, Consumer, Queue | Producer, Consumer, Queue, Command, Receiver |
| 优点 | 解耦、可扩展、支持撤销 | 缓冲、削峰填谷 | 兼具两者优点,并产生协同效应 |
命令模式与队列的结合是现代软件架构中一个非常基础且重要的模式,是构建高可用、高性能、可扩展系统的基石之一。
