Python 命令模式

命令模式是一种行为设计模式,它将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。简单来说,命令模式把操作(比如打开文件、保存数据)包装成独立的对象,让你能够像处理数据一样处理操作。

生活中的类比

想象一下餐厅的点餐流程:

  • 顾客(客户端)不需要知道厨师如何做菜
  • 服务员(调用者)接收订单但不亲自烹饪
  • 订单(命令对象)包含了具体的菜品信息
  • 厨师(接收者)根据订单执行具体的烹饪操作

这种分工明确的模式就是命令模式在现实生活中的体现。


为什么需要命令模式

传统方式的局限性

在传统编程中,我们通常直接调用对象的方法:

实例

class Light:
    def turn_on(self):
        print("灯亮了")
   
    def turn_off(self):
        print("灯灭了")

# 直接调用
light = Light()
light.turn_on()
light.turn_off()

这种方式存在几个问题:

  1. 紧耦合:调用者需要知道接收者的具体接口
  2. 难以扩展:添加新功能需要修改现有代码
  3. 不支持撤销/重做:操作执行后无法回退

命令模式的优势

命令模式通过引入中间层(命令对象)解决了上述问题,提供了:

  • 解耦:调用者与接收者之间没有直接依赖
  • 可扩展性:容易添加新的命令
  • 支持撤销/重做:可以记录操作历史
  • 队列支持:可以将命令放入队列延迟执行

命令模式的核心组件

基本结构

命令模式包含四个核心角色:

角色 职责 示例
Command(命令接口) 声明执行操作的接口 execute(), undo()
ConcreteCommand(具体命令) 实现命令接口,绑定接收者 LightOnCommand, LightOffCommand
Receiver(接收者) 知道如何执行操作 Light, TV
Invoker(调用者) 存储并执行命令 RemoteControl

完整代码示例

基础实现

让我们通过一个智能家居的灯光控制例子来理解命令模式:

实例

from abc import ABC, abstractmethod
from typing import List

# 1. 命令接口
class Command(ABC):
    @abstractmethod
    def execute(self):
        pass
   
    @abstractmethod
    def undo(self):
        pass

# 2. 接收者 - 灯光设备
class Light:
    def turn_on(self):
        print("💡 灯光已打开")
   
    def turn_off(self):
        print("💡 灯光已关闭")

# 3. 具体命令 - 开灯命令
class LightOnCommand(Command):
    def __init__(self, light: Light):
        self.light = light
   
    def execute(self):
        self.light.turn_on()
   
    def undo(self):
        self.light.turn_off()

# 4. 具体命令 - 关灯命令
class LightOffCommand(Command):
    def __init__(self, light: Light):
        self.light = light
   
    def execute(self):
        self.light.turn_off()
   
    def undo(self):
        self.light.turn_on()

# 5. 调用者 - 遥控器
class RemoteControl:
    def __init__(self):
        self.command = None
        self.history: List[Command] = []
   
    def set_command(self, command: Command):
        self.command = command
   
    def press_button(self):
        if self.command:
            self.command.execute()
            self.history.append(self.command)
   
    def press_undo(self):
        if self.history:
            last_command = self.history.pop()
            last_command.undo()

# 客户端代码
if __name__ == "__main__":
    # 创建设备
    living_room_light = Light()
   
    # 创建命令
    light_on = LightOnCommand(living_room_light)
    light_off = LightOffCommand(living_room_light)
   
    # 创建遥控器
    remote = RemoteControl()
   
    # 测试开灯
    print("=== 测试开灯 ===")
    remote.set_command(light_on)
    remote.press_button()
   
    # 测试关灯
    print("\n=== 测试关灯 ===")
    remote.set_command(light_off)
    remote.press_button()
   
    # 测试撤销
    print("\n=== 测试撤销操作 ===")
    remote.press_undo()  # 撤销关灯,应该开灯
    remote.press_undo()  # 撤销开灯,应该关灯

运行结果:


高级应用示例

支持多设备的万能遥控器

让我们扩展之前的例子,创建一个支持多种设备的万能遥控器:

实例

# 新增设备 - 电视
class TV:
    def __init__(self, location: str):
        self.location = location
        self.is_on = False
        self.volume = 50
   
    def turn_on(self):
        self.is_on = True
        print(f"📺 {self.location}电视已打开")
   
    def turn_off(self):
        self.is_on = False
        print(f"📺 {self.location}电视已关闭")
   
    def set_volume(self, volume: int):
        self.volume = volume
        print(f"📺 {self.location}电视音量设置为 {volume}")

# 电视相关命令
class TVOnCommand(Command):
    def __init__(self, tv: TV):
        self.tv = tv
        self.previous_volume = 50
   
    def execute(self):
        self.previous_volume = self.tv.volume
        self.tv.turn_on()
   
    def undo(self):
        self.tv.turn_off()
        self.tv.volume = self.previous_volume

class TVVolumeUpCommand(Command):
    def __init__(self, tv: TV):
        self.tv = tv
        self.previous_volume = 50
   
    def execute(self):
        self.previous_volume = self.tv.volume
        self.tv.set_volume(min(100, self.tv.volume + 10))
   
    def undo(self):
        self.tv.set_volume(self.previous_volume)

# 宏命令 - 一键执行多个命令
class MacroCommand(Command):
    def __init__(self, commands: List[Command]):
        self.commands = commands
   
    def execute(self):
        for command in self.commands:
            command.execute()
   
    def undo(self):
        # 按相反顺序撤销
        for command in reversed(self.commands):
            command.undo()

# 改进的遥控器
class AdvancedRemoteControl:
    def __init__(self, slot_count: int = 4):
        self.on_commands: List[Command] = [None] * slot_count
        self.off_commands: List[Command] = [None] * slot_count
        self.history: List[Command] = []
   
    def set_command(self, slot: int, on_command: Command, off_command: Command):
        self.on_commands[slot] = on_command
        self.off_commands[slot] = off_command
   
    def press_on_button(self, slot: int):
        if self.on_commands[slot]:
            self.on_commands[slot].execute()
            self.history.append(self.on_commands[slot])
   
    def press_off_button(self, slot: int):
        if self.off_commands[slot]:
            self.off_commands[slot].execute()
            self.history.append(self.off_commands[slot])
   
    def press_undo(self):
        if self.history:
            last_command = self.history.pop()
            last_command.undo()

# 测试高级遥控器
def test_advanced_remote():
    print("=== 测试万能遥控器 ===")
   
    # 创建设备
    living_room_light = Light()
    bedroom_tv = TV("卧室")
   
    # 创建命令
    light_on = LightOnCommand(living_room_light)
    light_off = LightOffCommand(living_room_light)
    tv_on = TVOnCommand(bedroom_tv)
    tv_volume_up = TVVolumeUpCommand(bedroom_tv)
   
    # 创建宏命令 - 影院模式
    cinema_mode = MacroCommand([light_off, tv_on, tv_volume_up])
   
    # 设置遥控器
    remote = AdvancedRemoteControl()
    remote.set_command(0, light_on, light_off)      # 槽位0:灯光控制
    remote.set_command(1, tv_on, TVOnCommand(bedroom_tv))  # 槽位1:电视控制
    remote.set_command(2, cinema_mode, light_on)    # 槽位2:影院模式
   
    # 测试各种功能
    print("\n1. 打开灯光:")
    remote.press_on_button(0)
   
    print("\n2. 开启影院模式:")
    remote.press_on_button(2)
   
    print("\n3. 撤销操作:")
    remote.press_undo()

if __name__ == "__main__":
    test_advanced_remote()

命令模式的参数和选项

命令接口的扩展

在实际应用中,命令接口可以根据需要扩展更多功能:

实例

class AdvancedCommand(ABC):
    @abstractmethod
    def execute(self):
        """执行命令"""
        pass
   
    @abstractmethod
    def undo(self):
        """撤销命令"""
        pass
   
    @abstractmethod
    def redo(self):
        """重做命令"""
        pass
   
    @abstractmethod
    def can_execute(self) -> bool:
        """检查命令是否可以执行"""
        pass
   
    @abstractmethod
    def get_description(self) -> str:
        """获取命令描述"""
        pass

命令队列和延迟执行

命令模式天然支持命令队列,这在很多场景下非常有用:

实例

class CommandQueue:
    def __init__(self):
        self.queue: List[Command] = []
   
    def add_command(self, command: Command):
        self.queue.append(command)
   
    def process_commands(self):
        while self.queue:
            command = self.queue.pop(0)
            if command.can_execute():
                command.execute()
   
    def clear(self):
        self.queue.clear()

实践练习

练习 1:实现空调控制系统

尝试实现一个空调控制系统的命令模式:

实例

# 你的代码在这里
class AirConditioner:
    def __init__(self):
        self.temperature = 26
        self.is_on = False
   
    def turn_on(self):
        # 实现打开空调
        pass
   
    def turn_off(self):
        # 实现关闭空调
        pass
   
    def set_temperature(self, temp: int):
        # 实现设置温度
        pass

# 实现空调相关的命令类
# AirConditionerOnCommand
# AirConditionerOffCommand  
# TemperatureUpCommand
# TemperatureDownCommand

练习 2:实现文本编辑器的撤销功能

创建一个简单的文本编辑器,支持输入文本和撤销操作:

实例

class TextEditor:
    def __init__(self):
        self.content = ""
   
    def add_text(self, text: str):
        # 实现添加文本
        pass
   
    def delete_text(self, length: int):
        # 实现删除文本
        pass

# 实现文本编辑命令
# AddTextCommand
# DeleteTextCommand

常见问题解答

Q: 命令模式适用于什么场景?

A: 命令模式特别适合以下场景:

  • 需要将操作参数化(如按钮绑定不同功能)
  • 需要支持撤销/重做功能
  • 需要将操作放入队列中延迟执行
  • 需要记录操作日志
  • 需要支持事务操作

Q: 命令模式的缺点是什么?

A: 主要缺点包括:

  • 可能会创建大量的命令类
  • 增加了代码的复杂度
  • 对于简单操作可能显得过于繁琐

Q: 如何选择是否使用命令模式?

A: 考虑以下几点:

  • 如果只需要简单的方法调用,不要使用命令模式
  • 如果需要撤销/重做功能,强烈推荐使用
  • 如果系统需要高度解耦,命令模式是很好的选择
  • 如果操作需要排队或日志记录,命令模式很合适

总结

命令模式通过将操作封装成对象,实现了调用者与接收者的解耦,提供了强大的扩展能力。虽然会增加一些代码复杂度,但在需要撤销/重做、操作队列、日志记录等场景下,命令模式是不可或缺的设计模式。

关键要点:

  • 命令模式将"做什么"和"谁来做"分离
  • 支持撤销、重做、队列等高级功能
  • 易于扩展新的命令
  • 在 GUI 应用、事务系统、游戏开发中广泛应用