设计模式实战:命令模式(Command)

在很多系统中,你会遇到这样的需求:

  • 把"操作"当成对象传递
  • 支持操作的撤销(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)

简单直接。


十一、优缺点

✅ 优点

  • 解耦调用者与执行者
  • 支持撤销/重做
  • 支持队列、日志
  • 灵活扩展

❌ 缺点

  • 类数量增加
  • 简单场景可能过度设计

十二、什么时候使用命令模式?

适合:

  • 需要记录操作
  • 需要撤销/重做
  • 需要队列执行
  • 操作需要解耦

不适合:

  • 简单函数调用
  • 无需扩展

十三、一句话总结

命令模式的本质是:
把"操作"变成对象,从而实现可记录、可撤销、可扩展。

换句话说:

不要直接调用方法,把调用封装起来。


相关推荐
石榴树下的七彩鱼2 小时前
图片修复 API 接入实战:网站如何自动去除图片水印(Python / PHP / C# 示例)
图像处理·后端·python·c#·php·api·图片去水印
Polar__Star2 小时前
C#怎么操作Chart图表控件 C#如何用WinForms Chart控件绑定数据绘制统计图表【控件】
jvm·数据库·python
2401_897190552 小时前
CSS如何制作数字滚动效果_利用transform位移数字
jvm·数据库·python
2301_813599553 小时前
HTML图片怎么用UnoCSS对齐_UnoCSS原子化CSS图片对齐实战
jvm·数据库·python
m0_377618233 小时前
c++怎么在不加载整个大文件的情况下获取其SHA256校验值【进阶】
jvm·数据库·python
LN花开富贵3 小时前
【ROS】鱼香ROS2学习笔记二
linux·笔记·python·学习·嵌入式
qq_189807033 小时前
CSS如何实现纯CSS树状目录结构_利用-checked与递归思维构建交互节点
jvm·数据库·python
Micr0673 小时前
利用Werkzeug-Debug实现本地权限提升
python·web安全·网络安全
yanghuashuiyue3 小时前
langchain AI应用框架研究【开发部署-篇四】
python·langchain