Python 中的命令模式(Command Pattern)
命令模式是一种行为型设计模式,其核心目的是:
将一个请求(操作)封装为一个对象,从而让你可以参数化客户端的不同请求、将请求排队或记录请求日志,还支持可撤销的操作。
形象比喻:就像餐厅点餐——顾客(调用者)向服务员(调用者)下订单(命令对象),服务员把订单交给厨师(接收者)执行。订单可以被保存、排队、甚至取消。
命令模式的优点
- 解耦调用者和接收者:调用者不需要知道谁来执行命令
- 支持撤销/重做(Undo/Redo):最经典的应用
- 支持命令队列、延迟执行、日志记录
- 易于扩展新命令:符合开闭原则
- 可以组合命令(宏命令)
典型应用场景
- GUI 按钮、菜单操作(点击执行命令)
- 撤销/重做功能(文本编辑器、绘图软件)
- 事务系统(数据库操作回滚)
- 任务队列、线程池
- 宏录制(一系列命令组合执行)
- 远程控制、日志记录
Python 实现示例:带撤销功能的文本编辑器
from abc import ABC, abstractmethod
from typing import List
# 接收者(Receiver):真正执行操作的对象
class TextEditor:
def __init__(self, text: str = ""):
self.text = text
def insert(self, position: int, content: str):
self.text = self.text[:position] + content + self.text[position:]
print(f"插入文本: '{content}' → 当前文本: '{self.text}'")
def delete(self, start: int, end: int):
deleted = self.text[start:end]
self.text = self.text[:start] + self.text[end:]
print(f"删除文本: '{deleted}' → 当前文本: '{self.text}'")
return deleted # 返回被删除的内容,用于撤销
def __str__(self):
return self.text
# 命令接口(Command)
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
# 具体命令:插入命令
class InsertCommand(Command):
def __init__(self, editor: TextEditor, position: int, content: str):
self.editor = editor
self.position = position
self.content = content
def execute(self):
self.editor.insert(self.position, self.content)
def undo(self):
# 撤销插入就是删除刚插入的内容
self.editor.delete(self.position, self.position + len(self.content))
# 具体命令:删除命令
class DeleteCommand(Command):
def __init__(self, editor: TextEditor, start: int, end: int):
self.editor = editor
self.start = start
self.end = end
self.deleted_content = None # 执行时保存被删内容
def execute(self):
self.deleted_content = self.editor.delete(self.start, self.end)
def undo(self):
if self.deleted_content is not None:
self.editor.insert(self.start, self.deleted_content)
# 调用者(Invoker):管理命令历史,支持撤销
class CommandManager:
def __init__(self):
self.history: List[Command] = []
self.redo_stack: List[Command] = []
def execute_command(self, command: Command):
command.execute()
self.history.append(command)
self.redo_stack.clear() # 新命令后,清空重做栈
def undo(self):
if not self.history:
print("没有可撤销的操作")
return
command = self.history.pop()
command.undo()
self.redo_stack.append(command)
print("撤销成功")
def redo(self):
if not self.redo_stack:
print("没有可重做的操作")
return
command = self.redo_stack.pop()
command.execute()
self.history.append(command)
print("重做成功")
# 客户端使用
if __name__ == "__main__":
editor = TextEditor("Hello World")
manager = CommandManager()
print(f"初始文本: '{editor}'\n")
# 执行插入
insert_cmd = InsertCommand(editor, 5, ", Python")
manager.execute_command(insert_cmd)
# 输出: Hello, Python World
# 执行删除
delete_cmd = DeleteCommand(editor, 5, 13) # 删除 ", Python"
manager.execute_command(delete_cmd)
# 输出: Hello World
print("\n--- 开始撤销 ---")
manager.undo() # 撤销删除 → 恢复 ", Python"
manager.undo() # 撤销插入 → 回到 "Hello World"
print("\n--- 开始重做 ---")
manager.redo() # 重做插入
manager.redo() # 重做删除
输出示例:
初始文本: 'Hello World'
插入文本: ', Python' → 当前文本: 'Hello, Python World'
删除文本: ', Python' → 当前文本: 'Hello World'
--- 开始撤销 ---
撤销成功 # 恢复到 Hello, Python World
撤销成功 # 恢复到 Hello World
--- 开始重做 ---
重做成功 # 插入 → Hello, Python World
重做成功 # 删除 → Hello World
Pythonic 简化版:函数作为命令
当命令简单时,可以用 callable(函数/闭包)代替类:
class SimpleCommandManager:
def __init__(self):
self.history = []
self.redo_stack = []
def execute(self, do_func, undo_func):
do_func()
self.history.append((do_func, undo_func))
self.redo_stack.clear()
def undo(self):
if self.history:
do_func, undo_func = self.history.pop()
undo_func()
self.redo_stack.append((do_func, undo_func))
def redo(self):
if self.redo_stack:
do_func, undo_func = self.redo_stack.pop()
do_func()
self.history.append((do_func, undo_func))
# 使用
text = "Hello"
manager = SimpleCommandManager()
def insert():
nonlocal text
text += " World"
print("→", text)
def remove():
nonlocal text
text = text[:-6]
print("←", text)
manager.execute(insert, remove)
manager.execute(lambda: print("额外操作"), lambda: None)
manager.undo()
manager.undo()
manager.redo()
命令模式结构总结
| 角色 | 说明 |
|---|---|
| Command | 抽象命令接口(execute/undo) |
| ConcreteCommand | 具体命令(InsertCommand、DeleteCommand) |
| Receiver | 接收者(TextEditor) |
| Invoker | 调用者(CommandManager) |
| Client | 创建命令并设置到调用者 |
命令模式 vs 其他模式对比
| 模式 | 目的 | 是否支持撤销 | 典型场景 |
|---|---|---|---|
| 命令 | 封装请求,支持撤销/队列 | 是 | 编辑器、事务、宏 |
| 策略 | 算法可替换 | 否 | 支付、排序 |
| 观察者 | 状态变化广播通知 | 否 | 事件监听 |
| 责任链 | 请求沿链传递 | 否 | 处理流程 |
Python 中的实用建议
- 简单命令:用 lambda 或函数闭包(最 Pythonic)
- 复杂命令:用类实现,支持状态保存
- 宏命令:创建一个 CompositeCommand,内部持有多个子命令
- 常与观察者模式结合(命令执行后通知 UI 更新)
class MacroCommand(Command):
def __init__(self, commands: List[Command]):
self.commands = commands
def execute(self):
for cmd in self.commands:
cmd.execute()
def undo(self):
for cmd in reversed(self.commands):
cmd.undo()
命令模式是实现可撤销操作和操作历史的基石,在编辑器、游戏存档、事务系统中广泛应用。
如果你想看更多实战例子(如 GUI 按钮命令、线程池任务队列、宏录制、结合队列的延迟执行),或者如何与备忘录模式结合实现多级撤销,欢迎继续问!