在很多系统中,你会遇到这样的需求:
- 把"操作"当成对象传递
- 支持操作的撤销(Undo)
- 支持操作队列 / 延迟执行
- 支持日志记录与重放
例如:
- 编辑器的撤销/重做
- 任务队列(异步执行)
- 按钮点击绑定不同行为
- 审批操作记录
如果直接写函数调用:
python
light.turn_on()
light.turn_off()
看起来简单,但问题是:
- 操作无法记录
- 无法撤销
- 无法统一调度
这时候就需要 ------ 命令模式(Command)。
一、命令模式解决什么问题?
一句话:
将请求封装为对象,从而使你可以用不同的请求对客户进行参数化。
关键词:
- 请求封装
- 解耦调用者与执行者
- 支持撤销/重做
- 支持队列
二、核心角色
命令模式包含四个角色:
1️⃣ Command(命令接口)
定义执行操作的接口。
2️⃣ ConcreteCommand(具体命令)
实现具体操作,绑定接收者。
3️⃣ Receiver(接收者)
真正执行动作的对象。
4️⃣ Invoker(调用者)
触发命令执行。
结构:
Invoker → Command → Receiver
三、一个典型场景:智能家居控制
我们用"控制灯"作为例子。
四、Python 实现命令模式
1️⃣ 接收者(Receiver)
python
class Light:
def turn_on(self):
print("灯打开")
def turn_off(self):
print("灯关闭")
2️⃣ 命令接口
python
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self):
pass
3️⃣ 具体命令
python
class TurnOnCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_on()
class TurnOffCommand(Command):
def __init__(self, light: Light):
self.light = light
def execute(self):
self.light.turn_off()
4️⃣ 调用者(Invoker)
python
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command: Command):
self.command = command
def press_button(self):
self.command.execute()
5️⃣ 使用方式
python
light = Light()
on_command = TurnOnCommand(light)
off_command = TurnOffCommand(light)
remote = RemoteControl()
remote.set_command(on_command)
remote.press_button()
remote.set_command(off_command)
remote.press_button()
输出:
灯打开
灯关闭
五、命令模式的核心价值
命令模式本质是:
把"操作"变成对象。
这样带来的好处:
- 操作可以存储
- 操作可以传递
- 操作可以组合
- 操作可以撤销
六、实现撤销(Undo)
命令模式一个重要能力:撤销操作。
修改命令接口:
python
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
实现:
python
class TurnOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
Invoker 记录历史:
python
class RemoteControl:
def __init__(self):
self.history = []
def execute(self, command):
command.execute()
self.history.append(command)
def undo(self):
if self.history:
command = self.history.pop()
command.undo()
使用:
python
remote = RemoteControl()
cmd = TurnOnCommand(light)
remote.execute(cmd)
remote.undo()
七、命令模式 vs 策略模式
很多人会混淆。
区别很关键:
| 对比点 | 命令模式 | 策略模式 |
|---|---|---|
| 关注点 | 请求封装 | 算法替换 |
| 是否记录操作 | 是 | 否 |
| 是否支持撤销 | 是 | 否 |
一句话:
策略是"怎么做",
命令是"做什么"。
八、命令模式 vs 观察者模式
| 对比点 | 命令模式 | 观察者模式 |
|---|---|---|
| 调用方式 | 主动触发 | 被动通知 |
| 关系 | 一对一 | 一对多 |
一句话:
命令是"我让你做",
观察者是"发生了你自己处理"。
九、真实项目中的应用
非常常见:
1️⃣ 任务队列
例如:
- Celery
- 异步任务系统
每个任务本质就是一个命令。
2️⃣ 撤销/重做系统
例如:
- 文本编辑器
- 图形编辑器
3️⃣ 审计日志
记录用户操作:
python
delete_user(1001)
可以记录为命令对象,后续回放。
4️⃣ UI 按钮绑定
按钮:
python
button.onClick = command.execute
十、更 Pythonic 的写法(函数版)
Python 中可以不用类。
直接用函数:
python
def turn_on():
print("灯打开")
def turn_off():
print("灯关闭")
Invoker:
python
class Remote:
def __init__(self):
self.commands = []
def execute(self, cmd):
cmd()
self.commands.append(cmd)
简单直接。
十一、优缺点
✅ 优点
- 解耦调用者与执行者
- 支持撤销/重做
- 支持队列、日志
- 灵活扩展
❌ 缺点
- 类数量增加
- 简单场景可能过度设计
十二、什么时候使用命令模式?
适合:
- 需要记录操作
- 需要撤销/重做
- 需要队列执行
- 操作需要解耦
不适合:
- 简单函数调用
- 无需扩展
十三、一句话总结
命令模式的本质是:
把"操作"变成对象,从而实现可记录、可撤销、可扩展。
换句话说:
不要直接调用方法,把调用封装起来。