Python 命令模式

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 按钮命令、线程池任务队列、宏录制、结合队列的延迟执行),或者如何与备忘录模式结合实现多级撤销,欢迎继续问!

文章已创建 3511

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部